天天看点

java多线程之Object中关于线程的方法

java多线程之Object中关于线程的方法

友情提示:因为方法只是用来展示代码的,有些是伪代码。所以创建方法的时候一定要有含义,不能像我一样只是一个字母就代替了。(本文档种方法纯属比较懒)

在java中Object是所有类的父类,所以Object的方法能被所有类使用,在Object中提供了许多关于线程的类。我们来逐一的看一下

1.wait()函数

wait()函数被调用时该线程会被阻塞挂起,除非两种情况可以解除挂起。一.其他线程调用了该共享对象的notify()或者是notifyAll()方法。二.其他线程调用该线程的interrupt()方法,抛出InterruptedException异常。

如果调用wait()方法没有事先获取对象监视器的锁,调用的时候回抛出IllegalMonitorStateException异常

那么怎样获得共享变量的监视器锁呢?

(1)执行synchronized同步代码块时,使用该变量作为参数。synchronized在线程中是同步锁,用来保证资源同步的。后面会有关于锁的内容。

synchronized (共享变量){
    //同步内容
}
           

(2) 调用该对象的方法,并且方法使用synchronized修饰

public synchronized  void f(){
    //同步内容
}
           

在我们使用同步锁的时候需要预防虚假唤醒 ,所谓虚假唤醒就是–线程从挂起状态到可运行状态(就是被唤醒),该线程并没有被其他线程调用notify()或者是notifyAll()方法,没有被被中断,没有等待超时就是虚假唤醒。

虽然虚假唤醒很少发生,但是为了避免出现,需要不断的去测试该线程被唤醒的条件是否满足。不满足继续等待。这里有一个生产消费模式,可以查到,后面有时间的话也会写一写的。生产消费模式可以帮助我们更好的理解这里的内容。

synchronized (obj){
    while(条件不满足){
        //循环内容
    }

}
           

2 wait(long timeout)函数

和wait()方法不同的是该方法设置了一个超时的参数。如果该线程在指定的时间(参数中的时间,注意是毫秒值)内没有被调用notify()或者notifyAll()方法,那么会因为超时而返回。如果参数设置为0,那么和wati()方法没有区别。如果参数为负数,那么会抛出一个IllegalArgumentException异常。

3 notify()

线程调用共享的notify()后,会唤醒该共享线程上通过wait()方法挂起的线程。该共享变量上可能会有多个线程,notity()会随机唤醒等待的线程。被唤醒的线程不能马上进入执行,只有唤醒它的线程释放了共享变量上的监视器锁后才可以返回。被唤醒不一意味着可以马上执行,该线程还需要和其他线程竞争该锁,只有竞争到该锁,才能继续执行。

4 notifyAll()

notifyAll()会唤醒所有该共享变量上由于调用wait系列而被挂起的线程。

下面通过例子来显示一下notify和notifyAll的区别:

public class ThreadTest {
    //创建资源 1
    private static volatile   Object  ob=new Object();


    public static void main(String[] args) throws InterruptedException {
        //创建线程 2
        Thread thA=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (ob){
                    System.out.println("线程A获取到锁");
                    try{
                        System.out.println("线程A开始等待");
                        ob.wait();
                        System.out.println("线程A等待结束");


                    }catch (InterruptedException e){
                        e.printStackTrace();


                    }
                }


            }
        });
        //创建线程 3
        Thread thB=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (ob){
                    System.out.println("线程B获取同步锁");


                    try {
                        System.out.println("线程B开始等待");
                        ob.wait();
                        System.out.println("线程B结束等待");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }


            }
        });


        //创建线程 4
        Thread  thC=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (ob){
                    System.out.println("线程C开始释放锁");
                    ob.notify();
                }


            }
        });


        //启动两个等待的线程 5
        thA.start();;
        thB.start();


        //让线程休眠 6
        Thread.sleep(1000);
        //启动唤醒线程 7
        thC.start();


        //等待线程结束
        thA.join();
        thB.join();;
        thC.join();


        System.out.println("执行结束");


    }
}
           

1.创建一个共享变量,目的是为了让下面的三个线程都是用同一个变量,这样可以保证是共享对象。

2.创建线程A让他调用wait()方法进行等待,等待的锁对象是1创建的。等待方法也是1调用的。

3.创建线程B他的目的和线程A的目的是一样的。

4.创建线程C用来唤醒等待的线程。

5.启动两个线程

6.让线程休眠,目的是为了让线程A和线程B同时进入等到状态

7.启动线程C,唤醒休眠的线程。

当线程C的唤醒方法为notify()的时候执行结果如下:

java多线程之Object中关于线程的方法

从结果中可以看出线程A已经执行完成,而线程B还处于等待状态。是因为,线程A和线程B同时调用了ob的wait()方法,所以被挂起。线程C前面的休眠就是为了让线程A和线程B同时进入等待状态。A和B同时进入等待状态后,调用C。C线程执行了唤醒操作,这里面唤醒了线程A(注意不是任何时候都是唤醒的A,因为线程是抢占式的,所以不确定唤醒谁。),所以A线程执行完毕,而B线程会陷入等待,直到下一次有其他线程唤醒。

当线程使用notifyAll()的时候,执行结果如下:

java多线程之Object中关于线程的方法

从执行结果可以等值notityAll()方法唤醒了全部线程,执行结果中B先获取到了资源所以会先执行完成,在B执行完成后线程A开始执行,线程A结束后,主线程开始执行。

注意:notifyAll()只能唤醒该方法前通过wait()挂起的线程,在notifyAll()后面挂起的线程不会被唤醒。