国外网站开发,wordpress不同page,王璞网站开发实战,宣传册设计与制作价格文章目录 一、线程安全问题-内存可见性二、等待通知2.1 wait()方法2.2 notify()方法 一、线程安全问题-内存可见性
import java.util.Scanner;public class Demo27 {private static int count0;//下面这段代码会出现内存的可见性问题//将从内存中读取count值的操作称为load 判… 文章目录 一、线程安全问题-内存可见性二、等待通知2.1 wait()方法2.2 notify()方法 一、线程安全问题-内存可见性
import java.util.Scanner;public class Demo27 {private static int count0;//下面这段代码会出现内存的可见性问题//将从内存中读取count值的操作称为load 判断操作称为cmp//load和cmp的执行速度差了好几个数量级在线程2开始执行代码提示输入数字时线程1的while循环已经执行了很多遍//java编译器会自动给代码进行优化//导致load只是第一次时真正从内存中读取count值其余都是从cpu的寄存器中读取//然而线程2修改count是在内存中进行修改线程1根本访问不到count的值//可以在变量前加上volatile关键字来提醒编译器不要优化public static void main(String[] args) {Thread t1new Thread(()-{while(count0) {//}System.out.println(t1 执行结束);});Thread t2new Thread(()-{Scanner scannernew Scanner(System.in);System.out.println(输入数字);countscanner.nextInt();});t1.start();t2.start();}}以上代码建立一个线程t1和线程t2线程t1中建立一个循环线程t2控制循环中的条件按照预期应该在t2中改变条件之后t1结束整个程序也应该结束但事实并非如此。 输入数字之后线程一仍然没有结束这是因为count0这个条件涉及到两个操作load和cmp先将count的值从内存中载入cpu的寄存器进行比较在线程二中还未输入数字时线程一的循环已经执行很多次了编译器发现这么多次count的值没有变化就会进行优化只有第一个load会从内存中读count的值剩余次数都是直接用寄存器中的值这样既使后来在线程二当中改变了内存中的count的值因为循环条件的count不从内存中读值因此访问不到修改后的count线程一就会继续循环从而程序就结束不了。如果不想让编译器进行优化可以在count前面加上volatile关键字即可。 另外上述的内存可见性问题加锁即包含在synchronized内也可以解决因为加锁是比较重量的load相对就不算慢了就不会触发优化。在循环内加上sout输出语句也是一个道理sout相对于load更慢无法触发优化自然也能解决上述的内存可见性的问题。 在网上查询资料还可以看到以一种JMMjava内存模型角度来理解该问题的解答 当t1执行的时候会从工作内存中读取count而不是从主内存中。 t2修改count会先修改工作内存再同步到主内存但由于t1没有重新读主内存导致t1没有感知到t2的修改。 二、等待通知
2.1 wait()方法
代码示例如下
public class Demo28 {public static void main(String[] args) throws InterruptedException {Object locker new Object();System.out.println(等待之前);//wait必须在synchronized内使用会解锁并且让线程进入阻塞等待状态synchronized (locker) {locker.wait();}System.out.println(等待之后);}
}
wait方法是object类的方法任何类对象都可以使用它只能在锁内使用如果执行wait方法会释放锁并且线程进入阻塞状态waiting。wait也有限时的参数加上时间会在限定时间内阻塞之后自动唤醒。 注意wait和sleep一样可以被interrupt打断并且清空标志位。
2.2 notify()方法
通过notify方法来唤醒wait方法进入阻塞的线程wait的线程状态从Waiting-Runnable-Blocked。 代码示例如下
public class Demo29 {public static void main(String[] args) throws InterruptedException {Object locker new Object();//1.wait和notify必须都在synchronized内//2.一个notify唤醒一个wait() 如果多个线程wait就使用多个notify或者notifyAll 这种唤醒时随机的//3.如果想唤醒指定线程也可以必须一对一写好锁对象//4.notify需要在wait之后Thread t1 new Thread(() - {System.out.println(t1 等待之前);synchronized (locker) {try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(t1 等待之后);});Thread t2 new Thread(() - {System.out.println(t2 通知之前);Scanner scanner new Scanner(System.in);synchronized (locker) {
//控制阻塞不输入数字t1仍是阻塞scanner.nextInt();locker.notify();}System.out.println(t2 通知之后);});t1.start();t2.start();}}notify必须也在synchronized之内并且一个notify只能唤醒对应的一个wait如果多个线程wait就使用多个notify或者notifyAll这种唤醒时随机的唤醒的前提是通过一个对象来调用的wait及notify方法。如果想唤醒指定线程也可以必须一对一写好锁对象。另外notify的使用必须在wait之后如果在wait之前使用notify不会有任何效果不过wait就没有办法唤醒了。 使用相应的锁对象进行一对一唤醒代码示例如下
public class Demo30 {public static void main(String[] args) throws InterruptedException {Object locker new Object();Object locker1 new Object();Thread t1 new Thread(() - {synchronized (locker) {System.out.println(t1等待之前);try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t1等待之后);}});Thread t2 new Thread(() - {synchronized (locker1) {System.out.println(t2等待之前);try {locker1.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t2等待之后);}});Thread t3 new Thread(() - {synchronized (locker) {//locker.notifyAll();locker.notify();}synchronized (locker1) {locker1.notify();}});t1.start();t2.start();Thread.sleep(1000);t3.start();}}