一、概念
1.定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
2.线程安全性:
原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作。
可见性:一个线程对主内存的修改可以及时的被其他线程观察到。
有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排排序的存在,该观察结果一般杂乱无序。
二、原子性-Atomic
1.原子性--Atomic包
原子英文单词为:atomic,刚刚好Java下定义了这样的类,比如:AtomicXXX:CAS;AtomicLong、LongAdder。
2.为什么要使用这个呢?或者说在什么场景下使用呢?
在并发场景中,当多线程需要对同一份资源做操作时,就会产生线程安全问题。以最简单的int i++为例,i++并不是原子操作,编译出来后分为三步:1,获取值;2,修改值;3,设置值。如果有多线程执行i++,则通常不会得到正确的结果。举例如下:
/**
* @author 繁荣Aaron
*/
public class ActiomTest {
static Logger logger = LoggerFactory.getLogger(ActiomTest.class);
private static int n = 0;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
n++;
try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { }
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
n++;
try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { }
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
logger.info("n = {}", n);
}
}
结果如下:
并不是我们所需要的结果:2000。所以必须用方法进行解决。
解决方式,如下:
1.使用synchronized关键字,具体使用参考先前的文章
(https://my.oschina.net/u/2380961/blog/1594040)。
2.JDK并发包里提供了很多线程安全的类。如:int对应线程安全的AtomicInteger。类似的还有:AtomicBoolean,AtomicLong,AtomicReference。
3.如何使用?
举例,如下:
public class AtomicExample1 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
//semaphore.acquire();
//add();
count.incrementAndGet();
//semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
//countDownLatch.countDown();
});
}
//countDownLatch.await();
executorService.shutdown();
System.out.println(count.get());
}
private static void add() {
int i = count.incrementAndGet();
// count.getAndIncrement();
//System.out.println(i);
}
}
一开始,去掉Semaphore 和CountDownLatch 两个工具类。总共执行5000次,那么输出的结果也应该是5000。但是真实结