1.线程状态
1.1 所有线程状态
for (Thread.State item : Thread.State.values()) {
System.out.println(item);
}
线程状态(6种)
NEW(新建状态,当线程被新建,但是为启动(start方法)之前的状态)
RUNNABLE(运行状态【运行】{得到时间片运行中状态}【就绪】{未得到时间片就绪状态})
BLOCKED(阻塞状态【如果遇到锁,线程就会变为阻塞状态等待另一个线程释放锁】)
WAITING(等待状态,没有明确结束时间的等待)
TIMED_WAITING(有明确结束时间的等待)
TERMINATED(销毁状态,当线程结束完成之后就会变成此状态)
1.2 线程状态转变
2.线程安全问题
概念:线程不安全指的是程序在多线程的执行结果不符合预期。
2.1 线程安全问题导致的原因:
1.抢占式执行
2.多个线程同时修改了同一个变量
3.操作是非原子性操作
什么是原子性?
我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还没有出来;B 是不是也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。
那我们应该如何解决这个问题呢?是不是只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。
有时也把这个现象叫做同步互斥,表示操作是互相排斥的。
4.内存可见性问题
可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到。
import java.time.LocalDateTime;
/**
* 内存可见性问题
*/
public class ThreadDemo17 {
//全局的变量
private static boolean flag=true;
public static void main(String[] args) {
//创建子线程1
Thread t1=new Thread(()->{
System.out.println("线程1开始执行:"+ LocalDateTime.now());
while(flag){
}
System.out.println("线程1结束执行!"+LocalDateTime.now());
});
t1.start();
Thread t2=new Thread(()->{
//休眠1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2:修改flag=false"+LocalDateTime.now());
flag=false;
});
t2.start();
}
}
线程1一直在执行,他并没有感知到全局变量flag的变化,这就是内存可见性问题(因为线程2早已把全局变量flag修改成另一个值了)。
5.指令重排序
编译器优化的本质是调整代码的执行顺序,在单线程下没问题,但在多线程下容易出现混乱,从而造成线程安全问题。
volatile
牺牲性能
volatile可以解决内存可见性问题和指令重排序问题,不能解决原子性问题
代码在写入 volatile 修饰的变量的时候:
1.改变线程工作内存中volatile变量副本的值
2.将改变后的副本的值从工作内存刷新到主内存
代码在读取 volatile 修饰的变量的时候:
1.从主内存中读取volatile变量的最新值到线程的工作内存中
2. 从工作内存中读取volatile变量的副本
import java.time.LocalDateTime;
/**
* 解决内存可见性问题
*/
public class ThreadDemo17 {
//全局的变量
private static volatile boolean flag=true;//加入volatile
public static void main(String[] args) {
//创建子线程1
Thread t1=new Thread(()->{
System.out.println("线程1开始执行:"+ LocalDateTime.now());
while(flag){
}
System.out.println("线程1结束执行!"+LocalDateTime.now());
});
t1.start();
Thread t2=new Thread(()->{
//休眠1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2:修改flag=false"+LocalDateTime.now());
flag=false;
});
t2.start();
}
}
使用volatile并不能完全解决线程安全问题