美文网首页并发编程
线程同步-synchronized-02

线程同步-synchronized-02

作者: 愤怒的奶牛 | 来源:发表于2019-08-16 17:56 被阅读0次
  1. 首先来看一下多线乱序访问导致的线程安全问题
 static int i = 0;
    public static void main(String[] args)  {
        new Thread(()->{
            i=+1; // 如果这里是 i++ 的话 不会出现安全问题,因为i++ 是原子操作
            System.out.println("第一个 i = " + i);
        }).start();
        new Thread(()->{
            i=+1;// i=i+1; 复合操作,先读 ,在写
            System.out.println("第2 个 i = " + i);
        }).start();
    }

输出结果:

输出结果.png
  • 从结果中可以看出,在多线程下,出现了数据错误的情况。

下面我们来分析一下多线程发生安全问题的原因。

  • JVM 运行时 内存管理(JVM 运行时区),jvm 将静态变量或者成员变量是存法在 方法区或者 堆内存中的,这两块区域都是 多线线程共享区域。
  • JMM 内存模型


    jmm.png

这里的共享变量就是 x。首先 线程 A 会 从 主内存中读取 x = 0 ,然后会拷贝一个线程本地本地变量 x= 0,然后 进行 加1 (x= x+1),最后会i 的值写回主内存中。也就上 经常说的复合操作(读,写)。

如果某一时刻 A 和 B 读取到 主内存中的 x 都相同 如 x = 0,然后各自在自己的 线程中计算 加1 (x = x+1),此时x = 1 ,A和B 同时将x写回 主内存,x= 1。其实我们在代码中对x 进行了 2 次加1 ,x 正确的值 是 2 ,但是现在是1 。这就导致了线程安全问题。(这里引申出一些概念 :可见性,原子性,一致性,后面将jvm 底层实现同步的时候解释)。

  • synchronized 同步

为了解决多线程安全问题,java 引入synchronized 同步锁。下面先来介绍一下 如何使用。

  1. 方法(对象级别)上的同步
    //共享变量
    private int i = 0;
    // 同步方法,锁的范围在对象范围
    public synchronized void add1() {
        i = i+1;
    }
// 这两种写法是等级的
   public void add2() {
        synchronized (this) {
            i = i+1;
        }
    }

 public static void main(String[] args) {
        HelloController helloController = new HelloController();
//        HelloController helloController2 = new HelloController();
// 下面两个线程都是 使用 的 helloController  对象,因为add1 方法 的锁的范围在 对象级别,只有使用同一个对象才能保证同步。
        new Thread(()->{
            helloController.add1();
            System.out.println("i = "+helloController.i);
        }).start();
        new Thread(()->{
            helloController.add1();
            System.out.println("i = "+helloController.i);
        }).start();

    }

输出:

输出结果 (2).png
  1. 类级别上的同步
private static int j = 0;
    // 静态方法上的同步,锁的范围在 类级别上
    public static synchronized void add3() {
        j = j+1;
    }
    
    // 这两个是等价的
    public void add4() {
        synchronized (HelloController.class) {
            j = j+1;
        }
    }

public static void main(String[] args) {
     new Thread(()->{
         HelloController.add3();
         System.out.println("j = "+ j);
     }).start();
     new Thread(()->{
         HelloController.add3();
         System.out.println("j = "+ j);
     }).start();

    }

synchronized 基本使用就到这里。

相关文章

网友评论

    本文标题:线程同步-synchronized-02

    本文链接:https://www.haomeiwen.com/subject/vbsxsctx.html