线程安全
多线程主要是为了提高我们应用程序的使用率。但同时,这会给我们带来很多安全问题!
线程安全问题发生的条件:
- 多线程环境下,即存在包括自己在内的索格线程
- 多线程环境下存在共享资源,且多线程操作共享资源
- 多个线程必须对该共享资源有非原子性操作
换个角度来看,如果状态不是共享的,或者不是可修改的,也就不存在线程安全问题,进而可以推理出保证线程安全的办法:
- 封装:加锁操作,我们可以将对象内部状态隐藏、保护起来
- 不可变:尽量不使用共享变量,将不必要的共享变量变成局部变量来使用,final 和 immutable 修饰
- 使用ThreadLocal为每一个线程建立一个变量的副本,各个线程间独立操作,互不影响
线程安全需要保证几个基本特性:
- 原子性:相关操作不会中途被其他线程干扰,一般通过同步机制实现
- 可见性:是一个线程修改了某个共享变量,其状态能够立即被其他线程知晓,通常被解释为将线程本地状态反映到主内存上,volatile 就是保证可见性的
- 有序性:是保证线程内串行语义,避免指令重排等
线程同步
当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行
这个时候,有个单线程模型下不存在的问题就来了:如果多个线程同时读写共享变量,会出现数据不一致的问题
举个栗子:
public class Main {
public static void main(String[] args) throws Exception {
Thread add = new AddThread();
Thread dec = new DecThread();
add.start();
dec.start();
add.join();
dec.join();
System.out.println(Counter.count);
}
}
class Counter {
public static int count = 0;
}
class AddThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) {
Counter.count += 1; }
}
}
class DecThread extends Thread {
public void run() {
for