线程安全定义:当多个线程访问某个类时,不管运行时环境采用
何种调度方式
或者这些进程如何交替运行,并且在主调程序中不需要额外的同步或者协同
,这个类都能表示出正确的行为,那么就称这个类为线程安全的。
线程安全主要体现在三个方面,分别是原子性、可见性、有序性。
一. 原子性
1: Atomic包
Java并发编程-无锁CAS与Unsafe类及其并发包Atomic
在Atomic包共提供了13个原子操作类,这些写原子操作类提供了一些简单高效的更新变量的方式。Atomic包里的类都是基于Unsafe类实现的包装类。这13个类主要分为
原子更新基本类型、原子更新数组、原子更新引用、原子更新属性
1. AtomicInteger原子类:基本类型
AtomicInteger 属于原子更新基本类型
AtomicInteger测试类:
package com.hust.concurrency.service.atomic;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import com.hust.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
/**
* @Description 原子操作类
* @since 2019年1月8日 下午8:37:54
* @author LiuLiBin
*/
@Slf4j
@ThreadSafe
public class AtomicIntegerTest {
//请求总数
public static int clientTotal = 1000;
//同时并发执行的线程数
public static int threadTotal = 50;
//测试并发 的计数值
public static AtomicInteger count = new AtomicInteger(0) ;
public static void main(String[] args) throws InterruptedException {
//定义线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//定义信号量 ,信号量为 50
final Semaphore semaphore = new Semaphore(threadTotal);
//定义计数器闭锁 ,期望所有请求请求完后统计计数结果
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
//放入请求,将请求线程全部放入线程池中
for (int i = 0; i < clientTotal; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
semaphore.acquire(); // 获取一个许可
add();
semaphore.release(); // 释放一个许可
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown(); // 计数器闭锁减1
}
});
}
//在所有线程请求执行完后 ,唤醒等待线程 ,此处涉及到countDownLatch 的实现机制
countDownLatch.await();
//关闭线程池
executorService.shutdown();
//打印计数值信息
log.info("count:{}",count.get());
}
/**
* 增加计数值
*/
private static void add() {
//以原子的方式将当前值加1,并返回加1后的值
count.incrementAndGet();
}
}
在上面代码中,count.incrementAndGet()方法是基于 compareAndSet 方法实现以原子的方式将当前值加1,并返回加1后的值。下面来看下AtomicInteger 的部分源码:
在上图中,compareAndSwapInt() 方法就是java 程序中的CAS指令,虚拟机在内部对这个方法进行了特殊处理,即时编译出来的是一条与平台相关的处理器CAS指令。
2. 原子更新数组、原子更新引用、原子更新属性
详见链接:原子操作类
3. AtomicStampReferenc:CAS的ABA问题
2: synchronized关键字
详见:深入理解Java并发之synchronized实现原理
二. 可见性
可见性是指当一个线程修改了共享变量的值,其他线程能够立即得到这个修改。volatile、synchronized 、final 修饰的变量都可以实现可见性
三.有序性
有序性是指对于单线程的执行代码,我们总是认为代码的执行是按顺序依次执行的,这样的理解并没有毛病,毕竟对于单线程而言确实如此,但对于多线程环境,则可能出现乱序现象,因为程序编译成机器码指令后可能会出现指令重排现象,重排后的指令与原指令的顺序未必一致,要明白的是,在Java程序中,倘若在本线程内,所有操作都视为有序行为,如果是多线程环境下,一个线程中观察另外一个线程,所有操作都是无序的,前半句指的是单线程内保证串行语义执行的一致性,后半句则指指令重排现象和工作内存与主内存同步延迟现象。
参考:
深入理解Java并发之synchronized实现原理
Java并发编程-无锁CAS与Unsafe类及其并发包Atomic