美文网首页JUC并发相关
1. 并发终结之线程安全

1. 并发终结之线程安全

作者: 涣涣虚心0215 | 来源:发表于2020-09-18 18:21 被阅读0次

    多个线程共享进程的资源,比如内存地址。
    同一进程中的线程访问相同的变量,并从同一个堆中分配对象,实现了良好的数据共享。但是如果没有明确的同步来管理共享数据,会导致线程安全问题。
    无状态的对象肯定是线程安全的,那么对于有可变状态的对象,只要有多于一个线程访问给定的状态变量,而且其中一个线程会写入该变量,此时必须使用同步来协调线程对改变量的访问。可以用synchronized提供独占锁,也可以使用volatileLock或者原子变量
    举个栗子:

    public class Test {
        private int count;
        public synchronized void addCount() {
            System.out.println(Thread.currentThread().getName() + " " + ++this.count);
        }
        public synchronized void minusCount() {
            System.out.println(Thread.currentThread().getName() + " " + --this.count);
        }
        public static void main(String[] args) {
            Test test = new Test();
            Thread t1 = new Thread(() -> {
                while (true) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.addCount();
                }
            }, "ThreadAdd");
    
            Thread t2 = new Thread(() -> {
                while (true) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    test.minusCount();
                }
            }, "ThreadMinus");
            t1.start();
            t2.start();
        }
    }
    没有synchronized的输出结果:
    ThreadAdd 1
    ThreadMinus -1
    ThreadAdd 0
    ThreadMinus -1
    ThreadAdd 0
    ThreadMinus -2
    加了synchronized的输出结果:
    ThreadAdd 1
    ThreadMinus 0
    ThreadMinus -1
    ThreadAdd 0
    ThreadAdd 1
    ThreadMinus 0
    

    在这个栗子里,两个线程对于count这个可变状态的修改,是属于竞态条件的。
    最常见的竞态条件是:check-then-act(比如new Object())以及 read-modify-save(比如count++)这样的复合操作。为了保证线程安全性,这种复合操作必须是原子的

    那么总结一下如何做到线程安全呢?

    1. 无状态:没有任何成员变量的类。
    2. 让类不可变:让状态不可变
      |-- 对于一个类,所有的成员变量应该都是私有的,而且所有的成员变量都应该加上final关键字。
      |-- 根本就不提供可修改成员变量的地方,同时成员变量也不作为方法的返回值。
    3. 加锁:保证操作的原子性,加锁同样保证内存可见性。
    4. volatile和CAS:保证类的可见性,最适合一个线程写,多个线程读的情形,且只适合一个变量的原子操作。
    5. 栈封闭:定义方法的局部变量。
    6. 安全的发布:不能让别的线程有机会拿到并修改本线程内部数据的机会,要么get()返回一个线程安全的容器,要么返回对象的副本,深拷贝的副本。
    7. ThreadLocal:让共享状态变成线程私有。

    相关文章

      网友评论

        本文标题:1. 并发终结之线程安全

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