Java并发编程之volatile、synchronized、yield、join
2017-07-20 19:00
441 查看
转载请注明来源-作者@loongshawn:http://blog.csdn.net/loongshawn/article/details/75574947,建议读者阅读原文,确保获得完整的信息
多线程可见。
禁止指令重排序。
重点对多线程可见这个属性做分析,字面意思即线程修改共享变量后,其他线程也能够获取到该共享变量修改后的值。但是要注意一点,这里隐含了一个条件“修改变量过程是否保证原子性”。
通过以下代码分析:
预期上述代码输出结果应该是3000,来看看实际运行结果:
每次运行结果都不一样,且都小于3000,已经在inc前加了volatile,线程获取到的inc值应该是最新的值,为什么还是不对?似乎忘了上面说的隐含条件“满足原子性”?inc++运算过程为:1、读取inc值;2、执行加1操作;3、写入内存。可见这个运算不是原子性的,线程可能读取的inc值是执行加1操作前的值,固每次运算结果都小于3000。
如何保证inc++操作具备原子性呢?利用同步代码块,在increase方法上添加synchronized,保证多线程对Test对象操作时,每一时刻只有一个线程能够执行increase方法,这样就保证了inc++的原子性:
下面来看看效果,符合预期:
对变量写操作不依赖当前值;
该变量没有包含在具有其他变量的不变式中。
其实,直白一点说就是volatile变量独立于程序状态、变量状态。其使用场景比如状态标记(线程执行状态)等。
这里对increase方法添加synchronized修饰,想着上述结果最终应该会输出3000,而实际结果:
仔细看能够看出端倪,这并不是并发操作,而是多线程对多个对象操作。并发操作可以理解为“一群运动员抢一个排球”,上述操作是“一群运动员每人一个排球,自己玩自己的”。所以正确使用synchronized关键字得注意场景,不是说对一个java类中的代码块添加修饰,这个代码块就同步了,如果存在多个对象,同步也是白搭。
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。
2、Java线程中yield与join方法的区别
3、Java并发编程:volatile关键字解析
volatile关键字
从两点理解:多线程可见。
禁止指令重排序。
重点对多线程可见这个属性做分析,字面意思即线程修改共享变量后,其他线程也能够获取到该共享变量修改后的值。但是要注意一点,这里隐含了一个条件“修改变量过程是否保证原子性”。
通过以下代码分析:
public class Test { public volatile int inc = 0; public void increase(){ inc++; } public static void main(String[] args){ final Test test = new Test(); List<Thread> list = new ArrayList<Thread>(); for (int i=0;i<3;i++){ Thread thread = new Thread(){ public void run(){ for (int j=0;j<1000;j++){ test.increase(); } } }; thread.start(); list.add(thread); } for (Thread thread : list){ try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(test.inc); } }
预期上述代码输出结果应该是3000,来看看实际运行结果:
第一次运行结果: 2798 第二次运行结果: 2794 第三次运行结果: 2847 第四次运行结果: 3000 第五次运行结果: 2985
每次运行结果都不一样,且都小于3000,已经在inc前加了volatile,线程获取到的inc值应该是最新的值,为什么还是不对?似乎忘了上面说的隐含条件“满足原子性”?inc++运算过程为:1、读取inc值;2、执行加1操作;3、写入内存。可见这个运算不是原子性的,线程可能读取的inc值是执行加1操作前的值,固每次运算结果都小于3000。
如何保证inc++操作具备原子性呢?利用同步代码块,在increase方法上添加synchronized,保证多线程对Test对象操作时,每一时刻只有一个线程能够执行increase方法,这样就保证了inc++的原子性:
public class Test { public int inc = 0; public synchronized void increase(){ inc++; } public static void main(String[] args){ final Test test = new Test(); List<Thread> list = new ArrayList<Thread>(); for (int i=0;i<3;i++){ Thread thread = new Thread(){ public void run(){ for (int j=0;j<1000;j++){ test.increase(); } } }; thread.start(); list.add(thread); } for (Thread thread : list){ try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(test.inc); } }
下面来看看效果,符合预期:
第一次运行结果: 3000 第二次运行结果: 3000 第三次运行结果: 3000 第四次运行结果: 3000 第五次运行结果: 3000
volatile使用场景
volatile关键字不保证原子性;对变量写操作不依赖当前值;
该变量没有包含在具有其他变量的不变式中。
其实,直白一点说就是volatile变量独立于程序状态、变量状态。其使用场景比如状态标记(线程执行状态)等。
synchronized关键字
被synchronized修饰的代码块在并发操作时只能被一个线程获取。这里会存在一个误区,通过下面例子说明:public class SYNTest { public int inc = 0; public synchronized void increase(){ inc++; } public static void main(String[] args){ for (int i=0;i<3;i++){ final Test test = new Test(); Thread thread = new Thread(){ public void run(){ for (int j=0;j<1000;j++){ test.increase(); } } }; thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(test.inc); } } }
这里对increase方法添加synchronized修饰,想着上述结果最终应该会输出3000,而实际结果:
1000 1000 1000
仔细看能够看出端倪,这并不是并发操作,而是多线程对多个对象操作。并发操作可以理解为“一群运动员抢一个排球”,上述操作是“一群运动员每人一个排球,自己玩自己的”。所以正确使用synchronized关键字得注意场景,不是说对一个java类中的代码块添加修饰,这个代码块就同步了,如果存在多个对象,同步也是白搭。
yield与join
yield与join均为Thread的成员方法,如下图所示:yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。
引用
1、java API文档2、Java线程中yield与join方法的区别
3、Java并发编程:volatile关键字解析
相关文章推荐
- java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- java并发编程学习:如何等待多个线程执行完成后再继续后续处理(synchronized、join、FutureTask、CyclicBarrier)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join) (r)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)(转)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java并发编程之线程生命周期、守护线程、优先级、关闭和join、sleep、yield、interrupt
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
- 【Java并发编程】Fork/join 并发编程模型,让多核cpu发挥最大优势。
- 关于JAVA并发库编程的复习(一):synchronized锁重入
- Java并发编程之volatile关键字解析
- 【Java并发编程】10、Java 理论与实践: 正确使用 Volatile 变量