1、线程控制

关键字:wait/notify/notifyAll、join、sleep、interrupt

线程控制讨论线程在调用了start()到执行完成中间阶段的行为,包含

  1. 线程阻塞和唤醒、超时等待

  2. 线程中断机制

JAVA篇:Java 多线程 (一) 线程控制_线程阻塞

 

1.1 线程阻塞和唤醒、超时等待

主要讨论join(),wait()、notify()和notifyAll(),以及yield()和sleep()。以及期间cpu资源及锁资源的情况,这里的锁仅仅考虑synchronized(Object)对象锁。

1.1.1 join() 方法

join()是线程的实例方法,有两种形式,thread.join()和tread.join(long timeout)。join方法会阻塞当前线程,等待指定线程运行完毕后才会被唤醒,或者如果设置了超时,等到超时后当前线程也会被唤醒

join()方法使得当前线程休眠,释放cpu资源,但是并不会释放锁

有说法说“其底层实现是wait()方法,会释放锁。”然后我写了一个测试代码,形成了死锁。wait()方法释放锁的相关讨论在后文,在这里先讨论join()方法运行过程中的情况。

join()的前缀是线程实例。如果要描述得清楚些则需要做一些假设。譬如说由线程A和t1,t1处于运行状态,在当前线程A调用t1.join()。那么线程A会无限阻塞,直到t1运行结束。

那么A在调用了t1.join()后等待t1的期间是否会释放资源呢?我感觉释放资源这个要往细了说,释放什么资源?释放cpu资源,释放cpu资源和全部锁资源,释放cpu资源和指定锁资源。

有说法join()方法调用之后会释放锁,总不可能是释放t1持有的锁吧,所以只能理解为释放当前线程A持有的锁。但是join方法不同,它是由t1调用的,也无法预见t1会需要什么锁资源,那么A调用t1.join()只可能是任意释放一个锁,或者说更加靠谱地释放全部锁。

我按照这个思路写了测试代码,但是主线程调用了join()并未释放任何锁,然后两个子线程都无法获得锁,造成了死锁。

为了防止死锁,我将join()方法设置了超时,使得代码可以运行,最后的代码和结果如下:

  
 /* 测试join() */
    public void test2(){
        /* 模拟共享资源 */
        Object res = new Object();
        Object res2 = new Object();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
​
        /*创建子线程1,共享资源res*/
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    System.out.println(df.format(new Date())+" part11:子线程1休眠结束,尝试请求res锁");
                    synchronized (res){
                        System.out.println(df.format(new Date())+" part12:子线程1获得res锁,后进入休眠");
                        Thread.sleep(3000);
                        System.out.println(df.format(new Date())+" part13:子线程1结束休眠,并释放res锁");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
​
        Thread t1 = new Thread(r1);
​
        t1.start();
​
        /*创建子线程2,共享资源res2*/
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    System.out.println(df.format(new Date())+" part21:子线程2休眠结束,尝试请求res2锁");
                    synchronized (res2){
                        System.out.println(df.format(new Date())+" part22:子线程2获得res2锁,后进入休眠");
                        Thread.sleep(3000);
                        System.out.println(df.format(new Date())+" part23:子线程2结束休眠,并释放res2锁");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
​
        Thread t2 = new Thread(r2);
​
        t2.start();
​
        /* 主线程持有锁,然后调用join */
       synchronized (res2){
            System.out.println(df.format(new Date())+" part01:主线程持有res2锁");
            synchronized (res){
                System.out.println(df.format(new Date())+" part02:主线程持有res锁");
                System.out.println(df.format(new Date())+" part03:主线程调用t1,t2的join()");
                try {
                    t1.join(10000);//有等待时限的join
                    t2.join(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(df.format(new Date())+" part04:主线程退出join,不再等待,释放锁res");
​
            }
           System.out.println(df.format(new Date())+" part05:主线程释放锁res2");
        }
​
        System.out.println(df.format(new Date())+" 测试结束。");
    }