多线程之线程可见性synchronized

synchronized的规定

  • 线程解锁前,必须把共享变量刷新到主内存
  • 线程加锁前将清空工作内存共享变量的值,需要从主存中获取共享变量的值。

加锁(synchronized 同步)的功能不仅仅局限于互斥行为,同时还存在另外一个重要的方面:内存可见性。我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且还希望确保当一个线程修改了对象状态后,其他线程能够看到该变化。而线程的同步恰恰也能够实现这一点。

内置锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果。为了确保所有的线程都能看到共享变量的最新值,可以在所有执行读操作或写操作的线程上加上同一把锁。下图示例了同步的可见性保证。

图片alt

当线程 A 执行某个同步代码块时,线程 B 随后进入由同一个锁保护的同步代码块,这种情况下可以保证,当锁被释放前,A 看到的所有变量值(锁释放前,A 看到的变量包括 y 和 x)在 B 获得同一个锁后同样可以由 B 看到。换句话说,当线程 B 执行由锁保护的同步代码块时,可以看到线程 A 之前在同一个锁保护的同步代码块中的所有操作结果。如果在线程 A unlock M 之后,线程 B 才进入 lock M,那么线程 B 都可以看到线程 A unlock M 之前的操作,可以得到 i=1,j=1。如果在线程 B unlock M 之后,线程 A 才进入 lock M,那么线程 B 就不一定能看到线程 A 中的操作,因此 j 的值就不一定是 1。

synchronized线程可见性安全案例

  1. package com.keytech.task;
  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. public class SynchronizedTestOne {
  5. public static void main(String[] args) {
  6. ExecutorService executorService = Executors.newCachedThreadPool();
  7. Rumenzz r=new Rumenzz();
  8. //线程1
  9. executorService.execute(()->{
  10. r.setAge(200);
  11. });
  12. //线程2
  13. executorService.execute(()->{
  14. System.out.println(r.getAge());
  15. });
  16. executorService.shutdown();
  17. }
  18. }
  19. class Rumenzz{
  20. private Integer age=0;
  21. public synchronized Integer getAge() {
  22. return age;
  23. }
  24. public synchronized void setAge(Integer age) {
  25. this.age = age;
  26. }
  27. }

以上代码是线程安全的,输出0200,因为线程1和线程2的执行顺序不一样。为了保证结果的一致性,需要控制线程的执行顺序。

  1. package com.keytech.task;
  2. import java.util.concurrent.CountDownLatch;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. /**
  6. * @className: SynchronizedTestOne
  7. * @description: TODO 类描述
  8. * @author: mac
  9. * @date: 2021/1/1
  10. **/
  11. public class SynchronizedTestOne {
  12. public static void main(String[] args) {
  13. ExecutorService executorService = Executors.newCachedThreadPool();
  14. Rumenzz r=new Rumenzz();
  15. CountDownLatch c=new CountDownLatch(1);
  16. executorService.execute(()->{
  17. try {
  18. c.await();
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println(r.getAge());
  23. });
  24. executorService.execute(()->{
  25. try {
  26. Thread.sleep(5000);
  27. c.countDown();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. r.setAge(200);
  32. });
  33. //关闭线程池
  34. executorService.shutdown();
  35. }
  36. }
  37. class Rumenzz{
  38. private Integer age=0;
  39. public synchronized Integer getAge() {
  40. return age;
  41. }
  42. public synchronized void setAge(Integer age) {
  43. this.age = age;
  44. }
  45. }

线程安全输出200

返回笔记列表
入门小站