美文网首页
线程安全性

线程安全性

作者: 王龙江_3c83 | 来源:发表于2019-03-19 10:00 被阅读0次

1. 线程安全性

1.1 定义

  • 线程安全:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协调,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
  • 线程不安全:如果一个类对象同时可以被多个线程访问,如果不做同步处理,可能表现出线程不安全现象(抛出异常、逻辑错误)。

1.2 原子性

1.2.1 定义

提供了互斥访问,同一时刻(时间段)只能有一个线程对它进行操作。避免脏读:数据读取过程中发生了写操作,数据发生了改变。

1.2.2 实现方法

1.2.2.1 synchronized

同一对象同时只能被 synchronized 一次。

  • 修饰动态方法
  • 修饰静态方法
  • 修饰代码块,synchronized(this){}
  • 修饰代码块,synchronized(引用类型){}
  • 修饰代码块,synchronized(类.class){}

1.2.2.2 ReentrantLock

1.2.2.3 ReentrantReadWriteLock

1.2.2.4 StampedLock

1.2.2.4.1 功能

读写锁虽然实现了读和读的并发,但读写之间是冲突的。

1.2.2.4.2 使用步骤
  • 主线程使用 new StampedLock() 方法创建 StampedLock 对象。
  • 写前使用 stampedLock.writeLock() 方法加写锁。写锁不会阻塞 tryOptimisticRead() 方法,但会阻塞 readLock()方法,改变 stamp。
  • 读前使用 long stamp = s1.tryOptimisticRead() 方法获取版本号,读取数据。
  • 确定版本号是否合法(一致),如果是则直接返回结果。
  • 如果不是,则将乐观锁升级为悲观锁,如果对象正在被修改,则本线程挂起。读取,释放读锁,返回结果。

1.2.2.5 CAS

public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
// var1 this
// var2 valueOffset
// var4 1
public final int getAndAddInt(Object var1, long var2, int var4) {
        // var5 变量当前值
        int var5;
        //如果变量当前值
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }
// 
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
if (var4 == var1.get(var1,var2)) {
    var4 = var5
    return true;
} else {
    return false;
} 

1.3 可见性

1.3.1 定义

一个线程对主内存的修改可以及时被其他线程观察到,例如取款和余额查询业务。

1.3.2 实现方法

如果没有 violate ,可能当前线程看似完成了赋值操作(即运行到下一步),但数据没同步进主存。

1.4 有序性

1.4.1 定义

一个线程观察其他线程的执行,由于指令重排序的存在,该观察结果一般杂乱无序。

1.4.2 实现方法

1.4.2.1 happens-before 原则

  • 同一线程内保证顺序性。
  • 解锁(Lock、synchronized)操作 happened-before 加锁操作。
  • violate 关键字修饰的变量在逻辑上但写操作,happened-before 随后对该变量的读操作。
  • 实现方式:内存屏障。

2. 线程安全策略

2.1 不可变对象

2.1.1 final 关键字

  • final 修饰的类不允许继承。
  • final 修饰的方法不允许重写。
  • final 修饰的变量在定义时必须初始化,后期不能修改,并且该变量在子类中也不能被修改,final 修饰的入参在函数中不能被修改,但 final 修饰对象的成员变量能被修改,修饰的容器内的元素能被修改。
  • 针对引用对象,使用 final 定义对象和其成员变量保证不变性。
  • 针对容器对象,有两种方式保证不变性:

2.2 线程封闭

2.2.1 ThreadLocal

2.2.1.1 定义

将对象封装到一个线程中,只有该线程能访问该对象。

2.2.1.2 实现步骤

public class Thread implements Runnable {
    ......
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ......
public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

2.2.1.3 使用步骤

  • 当Controller被调用时,调用RequestHolder的getId()静态方法,getId方法调用ThreadLocal<Long>对象的get方法。
  • 定义Filter,调用RequestHolder的add方法将当前线程的id添加进ThreadLocal。
  • 定义Interceptor,在完成请求响应后清除数据。
  • 注入 Filter 和Interceptor。
  • 好处:避免每次都从Request中取用户信息。

2.3 线程安全对象

2.3.1 定义

为了省略对于线程不安全类的并发访问需要进行的同步处理。

2.3.2 实现方法

2.3.2.1

  • StringBuilder:线程不安全。
  • StringBuffer:线程安全,方法使用synchronized关键字。

2.3.2.2

  • SimpleDateFormat:线程不安全。
  • DateTimeFormatter:线程安全,joda-time包。

2.3.2.3 AutomicXXX

线程安全,使用 CAS 实现。

2.4 被守护对象

2.4.1 定义

(lock、synchorized),对于共享变量的修改和读取使用锁或同步机制。

2.4.2 实现方法

参考本文章节 1.2.2。

参考资料

相关文章

  • EffectiveJava第十章第五节

    线程安全性的文档化 并非出现synchronized关键字就是线程安全性文档化了。实际上,一个类支持的线程安全性有...

  • String的线程安全

    线程安全性 说道有关string的线程安全性,大家想到的肯定时stringbuffer和stringbuilder...

  • Java并发编程 线程安全性

    什么是线程安全性 线程安全性:当多个线程访问某个类时,不管运行时采用何种调度方式或者这些线程将被如何交替执行,并且...

  • java并发编程实战2~3

    2 线程安全性 2.1 什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何...

  • 谈谈并发编程中的线程安全性

    1. 线程安全性 在单线程程序中,我们并不需要去考虑线程的安全性。但是在多线程程序中,由于多个线程要共享相同的内存...

  • 线程安全性(一)

    参考线程安全性总结 CountDownLatchCountDownLatch 可以阻塞线程并保证线程在满足某种特定...

  • 高并发编程03 ~ 线程安全性

    这节我们讨论一个话题:线程安全性 一、概念 线程安全性:当多个线程同时访问某个资源的时候,不管环境采用何种调度方式...

  • 线程安全性详解

    线程安全性 线程安全性定义: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并...

  • 理解Java中的线程安全及处理方法

    1. 线程安全性 1.1. 继承方式VS实现方式(掌握) 当多线程并发访问同一个资源时,会导致线程出现安全性的原因...

  • Effective STL 第12条

    容器的线程安全性 不能对容器的线程安全性抱太大的期望 对于stl容器,大部分容器只能保证1)支持多线程同时读2)支...

网友评论

      本文标题:线程安全性

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