volatile关键字
上个博客说过造成线程安全问题还有一个可解决的原因,内存可见性
volatile 修饰的变量,能够保证"内存可⻅性"
java
private static int flag=0;
public static void main(String[] args) {
Thread t0=new Thread(()->{
while (flag==0){
System.out.println("t0");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t0结束");
});
Thread t1 = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入 flag 的值: ");
flag = scanner.nextInt();
System.out.println("t2 结束");
});
t0.start();
t1.start();
}
上述代码运行起来结果不会出现t0结束,原因就是t0进程的对flag的load取数指令被编译器优化掉了,while循环很快,每次读到flag的值相同,而load的开销相比于compare比较指令大多,因此被优化为cmp
当t1对flag变量进⾏修改,此时t0感知不到flag的变化

加volatile 关键字
java
private static volatile int flag=1;
结果:

wait 和notify
线程之间是抢占式执⾏的,因此线程之间执⾏的先后顺序难以预知,但可以让调度到该线程时使用wait使该线程阻塞等待,直到其他线程使用notify唤醒
wait()/wait(long timeout): 让当前线程进⼊等待状态.
notify() / notifyAll(): 唤醒在当前对象上等待的线程.
注 意 : wait,notify, notifyAll 都是 Object 类的⽅法
wait()⽅法 :
使当前执⾏代码的线程进⾏等待.(把线程放到等待队列中)
过程
1.释放当前的锁
2.满⾜⼀定条件时被唤醒
3.重新尝试获取这个锁
java
Object locker = new Object();
Thread t1=new Thread(()->{
synchronized (locker) {
System.out.println("等待中");
locker.wait();
System.out.println("等待结束");
}
}
notify()⽅法:
1.唤醒等待的线程,如果有多个线程等待,则有线程调度器随机挑选出⼀个呈wait状态的线程。(并没有"先来后到")
2.在notify()⽅法后,当前线程不会⻢上释放该对象锁,要等到执⾏notify()⽅法的线程将程序执⾏ 完,也就是退出同步代码块之后才会释放对象锁。
java
Thread t2=new Thread(()->{
synchronized (locker){
System.out.println("notify之前");
locker.notify();
System.out.println("notify之后");
}
notifyAll()⽅法: notify⽅法只是唤醒某⼀个等待线程.使⽤notifyAll⽅法可以⼀次唤醒所有的等待线程.
wait与sleep区别
1.wait是Object类中的一个方法,sleep是Thread类中的一个方法;
2.wait必须在synchronized修饰的代码块或方法中使用,sleep方法可以在任何位置使用;
3.wait被调用后当前线程进入BLOCK状态并释放锁,并可以通过notify和notifyAll方法进行唤醒;sleep被调用后当前线程进入TIMED_WAIT状态,不涉及锁相关的操作;