线程安全性(一)

参考
线程安全性总结

CountDownLatch
CountDownLatch 可以阻塞线程并保证线程在满足某种特定条件下继续执行 (T从3减少1后T_A才能继续执行)
适合线程执行完之后进行其他处理

线程安全性(一)_第1张图片
CountDownLatch.png

Semaphore
阻塞进程,控制同一时间的并发量 适合控制同时并发的线程数

线程安全性(一)_第2张图片
Semaphore

线程安全性
定义:当多个线程访问某个类时,不管运行时采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么久称这个类时线程安全的

线程安全性的体现

  1. 原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作
  2. 可见性:一个线程对主内存的修改可以及时的被其他线程观察到
  3. 有序性:一个线程观察到其他线程中的指令执行顺序,由于指令 重排序的存在,该观察结果一般杂乱无序

Atomic实现时使用了unsafe的类

CAS的实现原理

线程安全性(一)_第3张图片
CAS.jpg

count.incrementAndGet();操作的底层是getAndAddInt(Object var1, Long var2, int var4) 第一个参数是传递进来的对象, 比如count.IncrementAndGet()中的count对象, 第二个参数是传递进来的值,比如说要执行2+1的操作,var2就是2, var4就是1,compareAndSwapInt(var1,var2,var5,var5+var4) 中 var5是通过调用底层this.getIntVolatile(var1,var2);得到的底层的值,如果没有别的线程来处理count这个变量,它正常返回的值应该是2,因此,compareAndSwapInt()中的var1就是count这个对象,var2就是当前值2,var5是从底层传过来的2,var5+var4就是从底层传回来的值加上想增加的量1
compareAndSwapInt()达到的效果: 如果当前的值和底层的值相同的话就更新值 (底层相当于是主内存, count是工作内存)
在一个死循环内不断刷新当前值,如果当前竞争不激烈的话修改成功的概率很高,否则的话修改失败的概率很高

线程安全性(一)_第4张图片
Atomic包.jpg

AtomicLongLongAdder更准确,在并发度不高的情况下使用较好

AtomicIntegerFileUpdater<> 指定更新某个类的某个字段的值,而这个字段必须是通过volatile修饰,同时不能是static的字段才可以

example:

public class AtomicExample5 {

    private static AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(AtomicExample5.class, "count");

    public volatile int count = 100;

    public int getCount(){
        return count;
    }

    private static AtomicExample5 example5 = new AtomicExample5();

    public static void main(String[] args) {
        if (updater.compareAndSet(example5, 100, 120)) {
            System.out.println("update success 1 "+ example5.getCount());
        }

        if (updater.compareAndSet(example5, 100, 120)) {
            System.out.println("update success 2 "+ example5.getCount());
        } else {
            System.out.println("update failed "+ example5.getCount());
        }
    }
}

Output:

update success 1 120
update failed 120

原因:第一次更新时已经将结果由100更新到了120,所以第二次会失败

CAS的ABA问题及AtomicStampReference解决

问题定义: 是指在CAS操作的时候,其他线程将变量的值A变成了B但是又改回了A,而本线程在进行比较的时候发现变量A没有变,于是 CAS就进行数据更新操作,但这与设计思想不符合,因此ABA问题的解决思路是对变量进行更新的时候把变量的版本号+1,这个时候只要变量被修改过,变量的版本号就发生变化

AtomicLongArray:多了对索引值进行更新

AtomicBoolean:

@ThreadSafe
public class AtomicExample6 {

    private static AtomicBoolean isHappened = new AtomicBoolean(false);

        // 请求总数
        public static int clientTotal = 5000;

        // 同时并发执行的线程总数
        public static int threadTotal = 200;

      //  public static AtomicLong count = new AtomicLong(0);

        public static void main(String[] args) throws InterruptedException {
            ExecutorService executorService = Executors.newCachedThreadPool();
            final Semaphore semaphore = new Semaphore(threadTotal);
            final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
            for (int i = 0; i {
                    try {
                        semaphore.acquire();       // 同时200个线程进行操作
                        test();
                        semaphore.release();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();                // 所有线程执行任务完毕才打印count
            executorService.shutdown();
            System.out.println("isHappened "+ isHappened.get());
        }

        private static void test(){
            if(isHappened.compareAndSet(false,true)){
                System.out.println("execute");
            }
        }

}

你可能感兴趣的:(线程安全性(一))