一把搞懂线程中stop、sleep、supend、yield、wait、notify

目录

前言

不废话直接上图

附加一个线程状态图吧不然看不懂

还是再附加一个文字吧看不懂直接往后看案例很清晰(下面这段话还回头再看)

1:stop()方法

1.1 stop()方法与java.lang.ThreadDeath异常

1.2 使用stop()释放锁给数据造成不一致的结果

2:suspend()方法

2.1 suspend()方法与resume()方法的使用

2.2 suspend()方法与resume()方法的缺点——独占

2.3 suspend()方法与resume()方法的缺点——数据不完整

3:yield()方法

4:wait方法

4.1 wait/notify机制的原理

4.2 wait()方法的基本使用

4.3 完整实现wait/notify机制

4.4 使用wait/notify机制实现list.size()等于5时的线程销毁

4.5 wait()方法:立即释放锁

5:sleep()方法

6:notify()方法

记得回头看线程的流程


前言

不废话直接上图

stop supend yield wait sleep notify
停止 暂停 让步 等待 睡眠 通知
线程都死了锁就不存在了 不让出锁 让出CPU 让出锁 不让出锁 不立即释放锁

附加一个线程状态图吧不然看不懂

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第1张图片

还是再附加一个文字吧看不懂直接往后看案例很清晰(下面这段话还回头再看)

1)创建一个新的线程对象后,调用它的start()方法,系统会为此线程分配CPU资源,此时线程处于runnable(可运行)状态,这是一个准备运行的阶段。如果线程抢占到CPU资源,则此线程就处于running(运行)状态。

2)runnable状态和running状态可相互切换,因为有可能线程运行一段时间后,其他高优先级的线程抢占了CPU资源,这时此线程就从running状态变成runnable状态。

线程进入runnable状态大体分为如下4种情况。

·调用sleep()方法后经过的时间超过了指定的休眠时间;

·线程成功获得了试图同步的监视器;

·线程正在等待某个通知,其他线程发出了通知;

·处于挂起状态的线程调用了resume恢复方法。

3)blocked是阻塞的意思,例如,如果遇到了一个I/O操作,此时当前线程由runnable运行状态转成blocked阻塞状态,等待I/O操作的结果。这时操作系统会把宝贵的CPU时间片分配给其他线程,当I/O操作结束后,线程由blocked状态结束,进入runnable状态,线程会继续运行后面的任务。

出现阻塞的情况大体分为如下5种。

·线程调用sleep()方法,主动放弃占用的处理器资源。

·线程调用了阻塞式I/O方法,在该方法返回前,该线程被阻塞。

·线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。

·线程等待某个通知(notify)。

·程序调用了suspend()方法将该线程挂起。此方法容易导致死锁,应尽量避免使用该方法。

4)run()方法运行结束后进入销毁阶段,整个线程执行完毕。


1:stop()方法

用stop()方法暴力停止线程

使用stop()方法可以强行停止线程,即暴力停止线程。

新建项目useStopMethodThreadTest,文件MyThread.java代码如下:


package testpackage;
public class MyThread extends Thread {
    private int i = 0;

    @Override
    public void run() {
        try {
            while (true) {
                i++;
                System.out.println("i=" + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

文件Run.java代码如下:


package test.run;

import testpackage.MyThread;

public class Run {

    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(8000);
            thread.stop();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

程序运行结果如图1-50所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第2张图片

图1-50 线程被暴力停止,stop运行图标呈灰色

由运行结果可以看出,线程被暴力停止了,这种方式就是1.11节介绍的第二种停止线程的方法——使用stop()方法强行终止线程。

stop()方法呈删除线程状态,是不再被采用的方法,原因是stop()方法容易造成业务处理的不确定性。例如,A线程执行如下业务:


增加数据1
增加数据2
增加数据3
增加数据4
增加数据5
增加数据6
增加数据7

这时在任意时机对A线程调用stop()方法,A线程并不能确定在哪里被停止了,造成数据增加得不完整。


1.1 stop()方法与java.lang.ThreadDeath异常

调用stop()方法时会抛出java.lang.ThreadDeath异常,但在通常情况下,此异常不需要显式地捕捉。

创建测试用的项目runMethodUseStopMethod,文件MyThread.java代码如下:


package testpackage;

public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            this.stop();
        } catch (ThreadDeath e) {
            System.out.println("进入了catch()方法!");
            e.printStackTrace();
        }
    }
}

文件Run.java代码如下:


package test.run;

import testpackage.MyThread;

public class Run {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

程序运行结果如图1-51所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第3张图片

图1-51 进入catch异常

stop()方法已经是作废的方法,因为如果暴力性地强制让线程停止,则一些清理性的工作可能得不到完成,或者数据添加不完整。

1.2 使用stop()释放锁给数据造成不一致的结果

对锁定的对象进行“解锁”,会导致数据得不到同步的处理,进而出现数据不一致的问题。本节将会讲解使用stop()释放锁给数据造成不一致性的结果,如果出现这样的情况,则程序处理的数据完全有可能遭到破坏,最终导致程序执行的流程是错误的,在此一定要注意。下面来看一个示例。

创建项目stopThrowLock,文件MyService.java代码如下:


package testpackage;

public class MyService {

    private String username = "a";
    private String passsword = "aa";

    synchronized public String getUsername() {
        return username;
    }

    synchronized public String getPasssword() {
        return passsword;
    }

    synchronized public void printString(String username, String passsword) {
        try {
            this.username = username;
            Thread.sleep(100000000);
            this.passsword = passsword;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

调用业务方法printString()的线程代码如下:


package testpackage;

public class MyThreadA extends Thread {

    private MyService object;

    public MyThreadA(MyService object) {
        super();
        this.object = object;
    }

    @Override
    public void run() {
        object.printString("b", "bb");
    }
}

输出username和passsword的线程代码如下:


package testpackage;

public class MyThreadB extends Thread {

    private MyService object;

    public MyThreadB(MyService object) {
        super();
        this.object = object;
    }

    @Override
    public void run() {
        System.out.println("username=" + object.getUsername());
        System.out.println("passsword=" + object.getPasssword());
    }
}

文件Run.java代码如下:


package test.run;

import testpackage.MyService;
import testpackage.MyThreadA;
import testpackage.MyThreadB;

public class Run {
    public static void main(String[] args) {
        try {
            MyService object = new MyService();
            MyThreadA threadA = new MyThreadA(object);
            threadA.start();
            Thread.sleep(100);
            MyThreadB threadB = new MyThreadB(object);
            threadB.start();
            Thread.sleep(3000);
            threadA.stop();
            System.out.println("stop()执行后,在下方开始打印username和passsword。");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如图1-52所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第4张图片

图1-52 强制停止线程造成数据不一致

由于stop()方法已经在JDK中被标明是“作废/过期”的方法,显然它在功能上具有缺陷,所以不建议在程序中使用stop()方法停止线程。


2:suspend()方法

暂停线程意味着此线程还可以恢复运行,在Java多线程中,可以使用suspend()方法暂停线程,使用resume()方法来恢复线程的执行。

2.1 suspend()方法与resume()方法的使用

本节将讲述suspend()方法与resume()方法的使用。

创建测试用的项目suspend_resume_test,文件MyThread.java代码如下:


package mythread;

public class MyThread extends Thread {

    private long i = 0;

    public long getI() {
        return i;
    }

    public void setI(long i) {
        this.i = i;
    }

    @Override
    public void run() {
        while (true) {
            i++;
        }
    }

}

文件Run.java代码如下:


package test.run;

import mythread.MyThread;

public class Run {

    public static void main(String[] args) {

        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(5000);
            // A段
            thread.suspend();
            System.out.println("A= " + System.currentTimeMillis() + " i="
                   + thread.getI());
            Thread.sleep(5000);
            System.out.println("A= " + System.currentTimeMillis() + " i="
                   + thread.getI());
            // B段
            thread.resume();
            Thread.sleep(5000);

            // C段
            thread.suspend();
            System.out.println("B= " + System.currentTimeMillis() + " i="
                   + thread.getI());
            Thread.sleep(5000);
            System.out.println("B= " + System.currentTimeMillis() + " i="
                   + thread.getI());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

程序运行结果如图1-54所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第5张图片

图1-54 暂停与恢复的测试

stop()方法用于销毁线程对象,如果想继续运行线程,则必须使用start()方法重新启动线程,而suspend()方法用于让线程不再执行任务,线程对象并不销毁,在当前所执行的代码处暂停,未来还可以恢复运行。

从控制台输出的时间上来看,线程的确被暂停了,而且可以恢复成运行状态。

2.2 suspend()方法与resume()方法的缺点——独占

如果suspend()方法与resume()方法使用不当,极易造成公共同步对象被独占,其他线程无法访问公共同步对象的结果。

创建suspend_resume_deal_lock项目,文件SynchronizedObject.java代码如下:


package testpackage;

public class SynchronizedObject {

    synchronized public void printString() {
        System.out.println("begin");
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("a线程永远 suspend了!");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }

}

文件Run.java代码如下:


package test.run;

import testpackage.SynchronizedObject;

public class Run {

    public static void main(String[] args) {
        try {
            final SynchronizedObject object = new SynchronizedObject();

            Thread thread1 = new Thread() {
                @Override
                public void run() {
                    object.printString();
                }
            };

            thread1.setName("a");
            thread1.start();

            Thread.sleep(1000);

            Thread thread2 = new Thread() {
                @Override
                public void run() {
                    System.out.println("thread2启动了,但进入不了printString()方法!只打印1个begin");
                    System.out.println("因为printString()方法被a线程锁定并且永远suspend暂停了!");
                    object.printString();
                    }
            };
            thread2.start();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

程序运行结果如图1-55所示。

另外一种独占锁的情况也需要格外注意,稍有不注意,就会掉进“坑”里。创建测试用的项目suspend_resume_LockStop,类MyThread.java代码如下:


package mythread;

public class MyThread extends Thread {
    private long i = 0;

    @Override
    public void run() {
        while(true) {
            i++;
        }
    }
}

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第6张图片

图1-55 独占并锁死printString()方法

类Run.java代码如下:


package test.run;

import mythread.MyThread;

public class Run {

    public static void main(String[] args) {

        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(1000);
            thread.suspend();
            System.out.println("main end!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

程序运行结果如图1-56所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第7张图片

图1-56 控制台输出main end信息

进程状态在控制台中呈红色按钮显示,说明进程并未销毁。虽然main线程销毁了,但是MyThread呈暂停状态,所以进程不会销毁。

但如果将线程类MyThread.java更改如下:


package mythread;

public class MyThread extends Thread {
    private long i = 0;

    @Override
    public void run() {
        while (true) {
            i++;
            System.out.println(i);
        }
    }
}

再次运行程序,控制台不输出main end,如图1-57所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第8张图片

图1-57 不输出main end信息

出现这种情况的原因是当程序运行到System.out.println(i)方法内部停止时,同步锁是不释放的,println()方法源代码如图1-58所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第9张图片

图1-58 锁不释放

当前PrintStream对象的println()方法一直呈“暂停”状态,并且“锁未释放”,而main()方法中的代码“System.out.println("main end!");”也需要这把锁,main线程并未销毁,造成迟迟不能输出main end。

虽然suspend()方法是过期作废的方法,但研究其过期作废的原因是很有必要的。

2.3 suspend()方法与resume()方法的缺点——数据不完整

在使用suspend()方法与resume()方法时也容易出现线程暂停,进而导致数据不完整的情况。

创建项目suspend_resume_nosameValue,文件MyObject.java代码如下:


package myobject;

public class MyObject {

    private String username = "1";
    private String passsword = "11";

    public void setValue(String u, String p) {
        this.username = u;
        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("停止a线程!");
            Thread.currentThread().suspend();
        }
        this.passsword = p;
    }

    public void printUsernamePasssword() {
        System.out.println(username + " " + passsword);
    }
}

文件Run.java代码如下:


package test;

import myobject.MyObject;

public class Run {

    public static void main(String[] args) throws InterruptedException {

        final MyObject myobject = new MyObject();

        Thread thread1 = new Thread() {
            public void run() {
                myobject.setValue("a", "aa");
            };
        };
        thread1.setName("a");
        thread1.start();

        Thread.sleep(500);

        Thread thread2 = new Thread() {
            public void run() {
                myobject.printUsernamePasssword();
            };
        };
        thread2.start();

    }

}

程序运行结果如图1-59所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第10张图片

图1-59 程序运行结果

程序运行结果出现值不完整的情况,所以在程序中使用suspend()方法要格外注意。

这两个方法被标识为作废过期的,想要实现对线程进行暂停与恢复的处理,可使用wait()、notify()或notifyAll()方法。


3:yield()方法

yield()方法的作用是放弃当前的CPU资源,让其他任务去占用CPU执行时间,放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。在本示例中可以取得运行的时间为结果,以测试yield()方法的使用效果。

创建t17项目,MyThread.java文件代码如下:


package extthread;

public class MyThread extends Thread {

    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < 50000000; i++) {
            // Thread.yield();
            count = count + (i + 1);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用时:" + (endTime - beginTime) + "毫秒!");
    }

}

文件Run.java代码如下:


package test;

import extthread.MyThread;

import extthread.MyThread;

public class Run {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }

}

程序运行结果如图1-60所示。

将代码:


// Thread.yield();

去掉注释,再次运行,结果如图1-61所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第11张图片


4:wait方法

wait/notify机制在生活中比比皆是,例如在就餐时就会出现,如图3-2所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第12张图片

图3-2 就餐的wait/notify机制

厨师和服务员的交互发生在“菜品传递台”上,在这期间需考虑以下几个问题。

1)厨师做完一个菜的时间未定,所以厨师将菜品放到“菜品传递台”上的时间也未定。

2)服务员取到菜的时间取决于厨师,所以服务员就有“等待”(wait)状态。

3)服务员如何取到菜呢,这取决于厨师,厨师将菜放在“菜品传递台”上,其实相当于一种通知(notify),这时服务员才可以拿到菜并交给就餐者。

在这个过程中出现了wait/notify机制。

需要说明的是,前面章节中多个线程之间也可以实现通信,原因是多个线程共同访问同一个变量,但那种通信机制不是wait/notify机制,两个线程完全是主动式地操作同一个共享变量,在花费读取时间的基础上,读到的值是不是想要的,并不能完全确定,例如,前面示例中添加了“Thread.sleep(2000);”代码,导致B线程不能退出,所以现在迫切需要引入wait/notify机制来满足上面的需求。

4.1 wait/notify机制的原理

拥有相同锁的线程才可以实现wait/notify机制,所以后面的描述中都是假定操作同一个锁。

wait()方法是Object类的方法,它的作用是使当前执行wait()方法的线程等待,在wait()所在的代码行处暂停执行,并释放锁,直到接到通知或被中断为止。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。通过通知机制使某个线程继续执行wait()方法后面的代码时,对线程的选择是按照执行wait()方法的顺序确定的,并需要重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此不需要try-catch语句捕捉异常。

notify()方法要在同步方法或同步块中调用,即在调用前,线程必须获得锁,如果调用notify()时没有持有适当的锁,则会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该锁的其他线程,如果有多个线程等待,则按照执行wait()方法的顺序对处于wait状态的线程发出一次通知(notify),并使该线程重新获取锁。需要说明的是,执行notify()方法后,当前线程不会马上释放该锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized同步区域后,当前线程才会释放锁,而呈wait状态的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕后,它会释放该对象锁,此时如果没有再次使用notify语句,那么其他呈wait状态的线程因为没有得到通知,会继续处于wait状态。

总结:wait()方法使线程暂停运行,而notify()方法通知暂停的线程继续运行。

4.2 wait()方法的基本使用

wait()方法的作用是使当前线程暂停运行,并释放锁

创建测试用的Java项目,名称为test1,类Test1.java代码如下:


package test;

public class Test1 {
    public static void main(String[] args) {
        try {
            String newString = new String("");
            newString.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

程序运行结果如图3-3所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第13张图片

图3-3 出现异常

出现异常的原因是没有“对象监视器”,即没有锁。

继续创建Test2.java文件,代码如下:


package test;

public class Test2 {

    public static void main(String[] args) {
        try {
            String lock = new String();
            System.out.println("syn上面");
            synchronized (lock) {
                System.out.println("syn第一行");
                lock.wait();
                System.out.println("wait下的代码!");
            }
            System.out.println("syn下面的代码");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

程序运行结果如图3-4所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第14张图片

图3-4 wait()方法后面的代码不执行了

线程不能永远等待下去,那样程序就停止不前,不能继续向下运行了,如何使呈wait状态的线程继续运行呢?答案就是使用notify()方法。

4.3 完整实现wait/notify机制

创建实验用的项目,名称为test2,类MyThread1.java代码如下:


package extthread;

public class MyThread1 extends Thread {
    private Object lock;

    public MyThread1(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                System.out.println("开始      wait time=" + 
                      System.currentTimeMillis());
                lock.wait();
                System.out.println("结束      wait time=" + 
                      System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

类MyThread2.java代码如下:


package extthread;

public class MyThread2 extends Thread {
    private Object lock;

    public MyThread2(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            System.out.println("开始notify time=" + System.currentTimeMillis());
            lock.notify();
            System.out.println("结束notify time=" + System.currentTimeMillis());
        }
    }
}

类Test.java代码如下:


package test;

import extthread.MyThread1;
import extthread.MyThread2;

public class Test {
    public static void main(String[] args) {
        try {
            Object lock = new Object();

            MyThread1 t1 = new MyThread1(lock);
            t1.start();

            Thread.sleep(3000);

            MyThread2 t2 = new MyThread2(lock);
            t2.start();

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

程序运行结果如图3-5所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第15张图片

图3-5 使用wait/notify方法的示例

从程序运行结果来看,3s后线程被通知(notify)唤醒。

4.4 使用wait/notify机制实现list.size()等于5时的线程销毁

下面通过一个示例来演示如何使用wait()与notify()来实现前面list.size()值等于5时的线程销毁。创建新的项目wait_notify_size5,类MyList.java代码如下:


package extlist;

import java.util.ArrayList;
import java.util.List;

public class MyList {

    private static List list = new ArrayList();

    public static void add() {
        list.add("anyString");
    }

    public static int size() {
        return list.size();
    }

}

类ThreadA.java代码如下:


package extthread;

import extlist.MyList;

public class ThreadA extends Thread {

    private Object lock;

    public ThreadA(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                if (MyList.size() != 5) {
                    System.out.println("wait begin "
                           + System.currentTimeMillis());
                    lock.wait();
                    System.out.println("wait end  "
                           + System.currentTimeMillis());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

类ThreadB.java代码如下:


package extthread;

import extlist.MyList;

public class ThreadB extends Thread {
    private Object lock;

    public ThreadB(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    MyList.add();
                    if (MyList.size() == 5) {
                        lock.notify();
                        System.out.println("已发出通知!");
                    }
                    System.out.println("添加了" + (i + 1) + "个元素!");
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

类Run.java代码如下:


package test;

import extthread.ThreadA;
import extthread.ThreadB;

public class Run {

    public static void main(String[] args) {

        try {
            Object lock = new Object();

            ThreadA a = new ThreadA(lock);
            a.start();

            Thread.sleep(50);

            ThreadB b = new ThreadB(lock);
            b.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

程序运行结果如图3-6所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第16张图片

图3-6 程序运行结果

日志信息wait end在最后输出,这说明notify()方法执行后并不立即释放锁,这个知识点在后面章节会进行详细介绍。

关键字synchronized可以将任何一个Object对象作为锁来看待,而Java为每个Object都实现了wait()和notify()方法,它们必须用在被synchronized同步的Object的临界区内。通过调用wait()方法可以使处于临界区内的线程进入等待状态,同时释放被同步对象的锁,而notify操作可以唤醒一个因调用了wait操作而处于wait状态中的线程,使其进入就绪状态,被重新唤醒的线程会试图重新获得临界区的控制权,也就是锁,并继续执行临界区内wait之后的代码。如果发出notify操作时没有处于wait状态中的线程,那么该命令会被忽略。

wait()方法可以使调用该方法的线程释放锁,然后从运行状态转换成wait状态,等待被唤醒。

notify()方法按照执行wait()方法的顺序唤醒等待同一锁的“一个”线程,使其进入可运行状态,即notify()方法仅通知“一个”线程。

notifyAll()方法执行后,会按照执行wait()方法相反的顺序依次唤醒全部的线程。

4.5 wait()方法:立即释放锁

执行wait()方法后,锁被立即释放。

创建实验用的项目,名称为waitReleaseLock,类Service.java代码如下:


package service;

public class Service {

    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait()");
                lock.wait();
                System.out.println("  end wait()");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

两个自定义线程类代码如图3-8所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第17张图片

图3-8 两个自定义线程类代码

运行类Test.java代码如下:


package test;

import extthread.ThreadA;
import extthread.ThreadB;

public class Test {

    public static void main(String[] args) {

        Object lock = new Object();

        ThreadA a = new ThreadA(lock);
        a.start();

        ThreadB b = new ThreadB(lock);
        b.start();

    }

}

程序运行结果如图3-9所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第18张图片

图3-9 wait()方法自动将锁释放


5:sleep()方法

sleep()方法:不释放锁

如果将wait()方法改成sleep()方法,就获得了同步的效果,因为sleep()方法不释放锁,如图3-10所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第19张图片

图3-10 sleep()方法不释放锁


6:notify()方法

执行notify()方法后,不立即释放锁,下面通过示例来验证。

创建新的项目notifyHoldLock,类MyService.java代码如下:


package service;

public class MyService {

    private Object lock = new Object();

    public void waitMethod() {
        try {
            synchronized (lock) {
                System.out.println("begin wait() ThreadName=" + 
                       Thread.currentThread().getName() + " time="
                       + System.currentTimeMillis());
                lock.wait();
                System.out.println("  end wait() ThreadName=" + 
                       Thread.currentThread().getName() + " time="
                       + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void notifyMethod() {
        try {
            synchronized (lock) {
                System.out.println("begin notify() ThreadName=" + 
                       Thread.currentThread().getName() + " time="
                       + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("  end notify() ThreadName=" + 
                       Thread.currentThread().getName() + " time="
                       + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

类MyThreadA.java和MyThreadB.java代码如下:


package extthread;

import service.MyService;

public class MyThreadA extends Thread {
    private MyService myService;

    public MyThreadA(MyService myService) {
        super();
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.waitMethod();
    }

}

package extthread;

import service.MyService;

public class MyThreadB extends Thread {
    private MyService myService;

    public MyThreadB(MyService myService) {
        super();
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.notifyMethod();
    }

}

类Test.java代码如下:


package test;

import extthread.MyThreadA;
import extthread.MyThreadB;
import service.MyService;

public class Test {

    public static void main(String[] args) throws InterruptedException {

        MyService myService = new MyService();

        MyThreadA a = new MyThreadA(myService);
        a.start();

        Thread.sleep(50);

        MyThreadB b = new MyThreadB(myService);
        b.start();

    }

}

程序运行结果如图3-11所示。

一把搞懂线程中stop、sleep、supend、yield、wait、notify_第20张图片

图3-11 notify()方法执行后锁不释放

通过对控制台输出的时间的分析,可以总结出:必须执行完notify()方法所在的同步synchronized代码块后才释放锁。


记得回头看线程的流程

你可能感兴趣的:(JAVA,java,jvm,servlet)