美文网首页
并发编程(四):线程安全解决方案-synchronized

并发编程(四):线程安全解决方案-synchronized

作者: codeMover | 来源:发表于2020-04-07 21:28 被阅读0次

临界区

  • 一段代码内如果存在对共享资源的多线程多写操作,称这段代码为临界区。

竞态条件

  • 多个线程在临界区内执行,由于大吗的执行序列不同导致结果无法预测,称之为发生竞态条件

i++(i是静态变量)执行时的JVM字节码指令

在JAVA内存模型中,完成静态变量的自增、自减操作需要在主存和工作内存进行数据交换。

  • 主内存:所有线程共享,主要包括方法区和堆
  • 工作内存:线程私有,主要包括线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和CPU工作的高速缓存区)
1. getstatic i//获取静态变量i的值
2. iconst_1 //准备常量1
3. iadd //i+1
4. putstatic i//将修改后的值存入静态变量i
静态变量修改时数据交换.png
@Slf4j(topic = "ants.UnSafeCount")
public class UnSafeCount {
    static int count = 0 ;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1") {
            @SneakyThrows
            @Override
            public void run() {
                for (int i=0;i<5000;i++){
                    count--;
                }
            }
        };
        Thread t2 = new Thread("t2") {
            @SneakyThrows
            @Override
            public void run() {
                for (int i=0;i<5000;i++){
                    count++;
                }
            }
        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.debug("count值为:{}",count);
    }
}

synchronized

  • 阻塞式
  • 采用互斥方式,保证同一时刻至多只有一个线程持有【对象锁】
  • 其他线程获取【对象锁】时阻塞
  • 拥有【对象锁】的线程可以安全的执行临界区的代码
  • 同步和互斥否可采用synchrinized
    互斥:保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
    同步 :线程执行的先后,顺序不同,需要一个线程等待其他线程运行到某个点
@Slf4j(topic = "ants.SafeCountSync")
public class SafeCountSync {
    // 使用synchronized,操作变成线程安全,此时输出值为0
    static int count = 0;
    static Object obj = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread("t1") {
            @SneakyThrows
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    synchronized (obj) {
                        count--;
                    }
                }
            }
        };
        Thread t2 = new Thread("t2") {
            @SneakyThrows
            @Override
            public void run() {
                for (int i = 0; i < 5000; i++) {
                    synchronized (obj) {
                        count++;
                    }
                }
            }
        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        log.debug("count值为:{}", count);
    }
}

线程安全时序图.png

小结:

  • synchronized实际上是用对象锁保证了临界区代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断。

synchronized修改

  • 修饰实例方法和静态方法
  • 修饰实例代码块和静态代码块

变量的线程安全分析

成员变量和静态变量线程安全

  • 如果没有被共享,则线程安全
  • 如果被共享了,根据变量的状态是否改变,分为
    - 如果只有读操作,则线程安全
    - 如果涉及读写操作,则这段代码是临界区,需要考虑线程安全

局部变量线程安全

  • 局部变量是线程安全的
  • 局部变量引用的对象则未必是线程安全的
    - 如果引用对象没有逃离方法的作用范围,则线程安全
    - 如果引用对象逃离方法的作用范围,则线程不安全

线程安全类

多个线程调用他们同一个实例的某个方法是线程安全的,但是组合调用不一定安全

  • String(内部属性不能修改,线程安全)
  • Integer、Long等包装类(内部属性不能修改,线程安全)
  • StringBuffer
  • Vector、Hashtable
  • JUC
  • Random

相关文章

网友评论

      本文标题:并发编程(四):线程安全解决方案-synchronized

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