java线程间的等待唤醒机制和volatile关键字

java线程间的等待唤醒机制和volatile关键字

线程间的等待唤醒机制
Object类中
void wait();   就是在其他线程调用此对象的notify()和notifyAll()方法前,导致当前线程等待。
void wait(long timeout);  就是在其他线程调用此对象的notify()和notifyAll()方法,或者超过指定的时间量前,导致当前线程等待
void notify();  唤醒在此对象监视器上等待的单个线程
void notify();  唤醒在此对象监视器上等待的所有线程
	在某个线程方法中对wait()和notify()的调用必须指定一个object对象,而且该线程必须拥有该object对象的monitor。而获取对象monitor最简单的办法就是,在对象上使用synchronized关键字。当调用wait()方法以后,该线程会释放掉对象锁,并进入sleep状态。而在其他线程调用notify()方法时,必须使用同一个object对象,notify()方法调用成功后,所在这个对象上的相应的等待线程将被唤醒。
wait()和sleep()的区别:
wait():一旦等待就会释放锁;可以设置等待时间量,也可以不设置时间量。
sleep():线程休眠后,不会释放锁;必须设置时间量
案例
//两个线程,一个线程打印字母A,B....Z,一个线程打印1,2,3....52,要输出结果是这种12A34B56C.....5152Z
public class MyTest {
    
    
    public static void main(String[] args) {
    
    
        MyObject myObject = new MyObject();
        CharMyThread th1 = new CharMyThread(myObject);
        NumThread th2 = new NumThread(myObject);
        th1.start();
        th2.start();
    }
}
class MyObject{
    
      //定义一个锁类
    int flag=1;   //定义一个标记
}
class CharMyThread extends Thread{
    
    
    private MyObject obj;
    public CharMyThread(MyObject obj) {
    
    
        this.obj = obj;
    }
    @Override
    public void run() {
    
    
        for(int i='A';i<='Z';i++){
    
    
            synchronized (obj){
    
    
                //如果字母先进来就等着,让数字先打印
                if(obj.flag!=2){
    
      //flag=1就等着
                    try {
    
    
                        obj.wait();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    System.out.print((char)i);
                    obj.flag=1;   //改一下标记
                    obj.notify();   //唤醒对方
                }
            }
        }
    }
}
class NumMyThread extends Thread{
    
    
    private MyObject obj;
    public NumMyThread(MyObject obj) {
    
    
        this.obj = obj;
    }
    @Override
    public void run() {
    
    
        for (int i = 1; i <= 52; i++) {
    
    
            if(obj.flag!=1){
    
       //flag=2就等着
                try {
    
    
                    obj.wait();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            System.out.print(i);  
            if(i%2==0){
    
        //打印完两次数字就唤醒对方
                obj.flag=2;    //改一下标记
                obj.notify();
            }
        }
    }
}
结果是

在这里插入图片描述

volatile关键字
volatile关键字是解决内存可见性问题
java可见性
	对于可见性,java提供了volatile关键字来保证可见性
	当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值
	而普通的共享变量不能保证可见性,因为普通共享变量被修饰之后,什么时候被写入主存时不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
	另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的刷新到主存当中,因此可以保证可见性。
voliatile关键字: 当多个线程进行操作共享数据时,可以保证内存中的数据可见。相较于synchronized是一种较为轻量级的同步策略,但是由于锁不同,对于多线程,不是一种互斥关系;不能保证变量状态的“原子性操作”。
public class MyTest3 {
    
    
    public static void main(String[] args) {
    
    
        MyRunnable myRunnable = new MyRunnable();
        Thread th = new Thread(myRunnable);
        th.start();
        while (true){
    
    
            if(myRunnable.isFlag()){
    
    
                System.out.println("进来了");
                    break;
            }
        }
    }
}
class MyRunnable implements Runnable{
    
    
    volatile boolean flag=false;
    public boolean isFlag() {
    
    
        return flag;
    }
    public void setFlag(boolean flag) {
    
    
        this.flag = flag;
    }
    @Override
    public void run() {
    
    
        try {
    
    
            Thread.sleep(50);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        flag=true;
        System.out.println("run方法里改了flag的值:"+flag);
    }
}
结果是

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_51717449/article/details/113006045