首页 文章 精选 留言 我的

精选列表

搜索[并发编程],共10000篇文章
优秀的个人博客,低调大师

Java并发编程笔记1-竞争条件&初识原子类&可重入锁

我们知道,在多线程访问一个共享变量的时候会发生安全问题。 首先看下面例子: public class Counter { private int count; public void add(){ try{ for (int i = 0;i<200;i++){ Thread.sleep(100); this.count++; System.out.println(this.count); } }catch (Exception e ){ e.printStackTrace(); } } } public class Test { public static void main(String[] args) { final Counter counter = new Counter(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); } } 运行结果如下: 如果没发生线程对数据的赃操作,3个线程执行,结果会累加到600的,看上面结果明显没有,并且出现一下重复的数据。这是因为存在3个线程同时操作同一个数字,导致输出重复数字。 解决办法: 1.在方法上加上synchronized关键字。如下图: 虽然结果不会出现重复,但是synchronized效率及其低下,运行速度明显被拖慢。原因是,for循环中的i变量是每个线程都有独自的局部变量,各不影响,但是每个线程都要排队进入这个方法,排队睡觉,这样就导致效率低下 2.在竞争条件上加synchronized,我们知道各个线程其实竞争的是count这个成员变量。因此在此地方加即可。如下图: 这样运行效率比方法一快了很多,因为省去了排队进入方法,排队睡觉。只需要排队取count值即可,这样效率比方法一快。 注意上图中,输出语句并不是竞争条件,并不一定要放在synchroized里面,这里放在里面是为了让线程取到值自增后立即输出,这样输出就不会发生混论,不发生抢占输出问题,一样能累加到600, 如果把输出放在synchronized外面会出现值有重复现象,因为累加后的值并没有立即输出,这样导致输出混乱,但仍然能加到600.知识输出不安全罢了。 3.使用原子类型,比如将上面的代码的int count类型改成AtomicInteger count类型,我们知道获取count的值然后再自加个1是可能会出现问题的,也就是结果出现重复数字。AtomicInteger类型是以同步的方法解决这个问题的。如下图: 结果如下图: 可以看到数字的输出没有严格的排队,但是数据确实给你保证的了,就是完整的加到600.这也恰恰因为不是严格的进行排队,才是的这种方法比前面两种方法的效率大大改进。 理论总结:synchronized是一种内部锁,就是所对象内部给我们提供的,因为每一个对象有一个状态变量,相当于一个锁,进入同步块,改变这个变量。别的线程进入之后就要判断这个变量有没有改变。 一个线程获取它本生已经持有的锁,这是可以成功的。我们知道多个线程同时抢占同一个锁它们是失败的。因为它们之间是互斥的。但是呢,一个线程再次获取一个自己已经拿过的锁是可以成功的,那么它是能够成功的。 看如下例子: public class Widget { public synchronized void doSth(){ } } public class ChildWidget extends Widget { @Override public synchronized void doSth() { super.doSth(); } } public class Test { public static void main(String[] args) { Widget w = new ChildWidget(); w.doSth(); } } 子类调用自己的方法的那个synchroized那个锁是w对象的这个锁,而在子类方法中的super.doSth()父类中的方法的synchronized的锁也是w对象的锁。因此不要被子类继承父类的方法中的锁所迷惑了。 因此这种方式叫做内部锁的可重入机制,也叫可重入锁。

优秀的个人博客,低调大师

《Java并发程编程艺术》重点总结

synchronized修饰的代码块同步执行,不在synchronized块中的代码会异步执行,因此推荐用同步代码块,而非同步整个方法。 synchronized修饰同步方法和代码块,一定是排队运行的(线程独占)。 多线程访问“共享变量”,才需要加同步。 同步不能继承。 当一个线程执行的代码出现异常,其持有的锁会自动释放。 synchronized加到static方法上是给Class类上锁。 synchronized加到实例方法上,是给对象上锁。 String常量池缓存,synchronized代码块加锁一般不使用String。 synchronized代码块具有volatile同步变量到主内存的功能。 wait()和notify()都是在同步方法、代码块中使用。 执行notify()后不会立即释放对象锁,等执行完synchronized代码块才释放,然后其他wait状态的线程才可以获得该对象锁。 线程间通信,可使用管道字节流、管道字符流;建立通信的管道两端,都是阻塞的,即写端无写入时,读端将会阻塞。类似于 Linux进程通信的pipe。 PipeInputStream/PipeOutputStream PipeReader/PipeWriter ReentrantReadWriteLock 具有读和写两个锁;读读共享锁、写写排他锁、读写排他锁(有写 就互斥)。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册