转载

JAVA并发编程-Thread和Object类中的重要方法详解

TODO:

1、为什么通信的方法wait()、nofity()、notifyAll()被定义在Object类里,而sleep被定义在Thread类里?

2、用三种方式实现生产者模式

3、join、sleep和wait期间,线程的状态分别是什么?为什么?

wait,notify,notifyAll()

1、作用、用法

阻塞阶段:

使用wait()后进入阻塞阶段,以下四种情况之一发生,才会被唤醒
复制代码
  • 另一个线程调用了这个对象的notify()方法且刚好唤醒本线程(因为notify方法唤醒哪个线程是不确定的)
  • 另一个线程调用了notifyAll()方法
  • wait(long timeout),等待时间超过了规定时间,如果传入0表示永久等待
  • 线程自身调用了interrupt(),因为wait()是会响应中断的

唤醒阶段

遇到中断

//普通情况
package com.company;

public class WaitAndNotify {
    public static Object object = new Object();

    static class WaitRun implements Runnable {

        @Override
        public void run() {
            synchronized (object) {
                System.out.println(System.currentTimeMillis() + ": " + Thread.currentThread().getName() + "开始执行了");
                //wait后丢掉锁
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //如果再次获得锁才会执行接下来的代码
                System.out.println(System.currentTimeMillis() + ": " + Thread.currentThread().getName() + "又重新获取到了锁");
            }
        }
    }

    static class NotifuRun implements Runnable {
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println(Thread.currentThread().getName() + "调用了notify(),");
                //为了看使用了notify后是立即释放锁,还是等执行完成后 释放锁
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new WaitRun());
        Thread t2 = new Thread(new NotifuRun());
        t1.start();
        //为了让t1先使用到了wait进入到阻塞态
        Thread.sleep(100);
        t2.start();
    }
}
复制代码
JAVA并发编程-Thread和Object类中的重要方法详解

从结果可以看出来,线程0的执行顺序是先wait,再被notify后,重新获得锁再继续执行完成的,并且线程1的释放锁不是在notify后就立即释放而是该线程执行完毕后释放。

notify和notifyAll()

/**
* 1、看notify()和notifyAll()的区别
* 2、观察notify()在所有线程都进入阻塞态前就执行的结果
*/
public class WaitNotifyAll implements Runnable {
   private static final Object RESOURCE = new Object();

   @Override
   public void run() {
       synchronized (RESOURCE) {
           System.out.println(Thread.currentThread().getName() + " get RESOURCE lock");
           try {
               System.out.println(Thread.currentThread().getName()+" waits");
               RESOURCE.wait();
               System.out.println(Thread.currentThread().getName()+" 'reget RESOURCE lock");
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }

   public static void main(String[] args) throws InterruptedException {
       Thread t1 = new Thread(new WaitNotifyAll());
       Thread t2 = new Thread(new WaitNotifyAll());
       Thread t3 = new Thread(new Runnable() {
           @Override
           public void run() {
               synchronized (RESOURCE){
                   RESOURCE.notifyAll();
//                    RESOURCE.notify();
                   System.out.println("Thread 3 notified");
               }
           }
       });

       t1.start();
       t2.start();
       //确保t1,t2都已经执行
       Thread.sleep(200);
       t3.start();
   }
}
复制代码

使用notifyAll()会唤醒所有使用RESOURCE锁的线程

JAVA并发编程-Thread和Object类中的重要方法详解

只释放当前monitor

/**
* 证明wait只释放当前的那把锁
*/
public class WaitNotifyReleaseOwnMonitor {
   private static final Object ResourceA = new Object();
   private static final Object ResourceB = new Object();

   public static void main(String[] args) {
       Thread t1 = new Thread(()->{
           synchronized (ResourceA) {
               System.out.println("T1 get ResourceA lcok");
               synchronized (ResourceB) {
                   System.out.println("T1 get ResourceB lock");
                   try {
                       System.out.println("T1 release ResourceA lock");
                       ResourceA.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
       }
       );

       Thread t2 = new Thread(()->{
           //让t1先运行起来,或得到A、B两把锁
           try {
               Thread.sleep(200);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

           synchronized (ResourceA) {
               System.out.println("T2 get ResourceA lock");
               System.out.println("T2 try to get ResourceB lock");
               synchronized (ResourceB) {
                   System.out.println("T2 get ResourceB lock");
               }
           }
       });
       t1.start();
       t2.start();
   }
}
复制代码

从结果可以看出来,某个对象只释放当前对应的monitor锁,T2没有获得到ResourceB的锁。

JAVA并发编程-Thread和Object类中的重要方法详解

sleep()

1、不释放锁,包括 synchronizedlock ,这是与wait最大的不同点

/**
* 考查sleep是否释放synchrd的monitor锁
*/
public class SleepDontReleaseMonitor implements Runnable{

   public static void main(String[] args) {
       SleepDontReleaseMonitor run = new SleepDontReleaseMonitor();
       Thread t1 = new Thread(run);
       Thread t2 = new Thread(run);
       t1.start();
       t2.start();
   }

   @Override
   public void run() {
       syn();
   }

   private synchronized void syn() {
       System.out.println(Thread.currentThread().getName()+" starts");
       try {
           Thread.sleep(5000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println(Thread.currentThread().getName()+" ends");
   }
}
复制代码

可以看出来,两个线程串行执行,sleep并不释放锁

JAVA并发编程-Thread和Object类中的重要方法详解

对于lock锁

/**
 * 考查sleep是否释放lock锁(不会)
 */
public class SleepDontReleaseLock implements Runnable{
    private static final Lock lock = new ReentrantLock();

    @Override
    public void run() {
        lock.lock();
        System.out.println(Thread.currentThread().getName()+" 获得了锁");
        try {
            Thread.sleep(5000L);
            System.out.println(Thread.currentThread().getName()+" sleep结束,苏醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        SleepDontReleaseMonitor run = new SleepDontReleaseMonitor();
        new Thread(run).start();
        new Thread(run).start();
    }
}
复制代码

结果和上一个是一致的。

JAVA并发编程-Thread和Object类中的重要方法详解

2、sleep方法响应中断

(1) 抛出InterruptedException

(2) 清除中断状态

/**
 * 每过1秒钟输出当前时间,被中断,观察
 * Thread.sleep()
 * TimeUnit.SECONDS.sleep()
 */
public class SleepInterrupted implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(new Date());
            try {
//                TimeUnit.MINUTES.sleep(1);
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("线程被中断了");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new SleepInterrupted());
        t1.start();
        Thread.sleep(3500);
        t1.interrupt();
        Thread.sleep(3500);
        t1.interrupt();
    }
}
复制代码
JAVA并发编程-Thread和Object类中的重要方法详解
  • (作用)让线程进入waiting状态,并且不占用cpu资源
  • (特点)不释放锁
  • (中断)休眠期间可以响应中断,会抛出InterruptedException并清除中断状态

join

1、由于有新的线程加入我们,所以我们要等其执行完再出发 尝试用main等待thread0、1执行完毕,注意是谁等谁(主线程等子线程)

普通用法

/**
 * 描述:     演示join,注意语句输出顺序,会变化。
 */
public class joinNormal {
    static class WaitRunnable implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"执行完毕");
        }
    }

    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(new WaitRunnable());
        Thread t2 = new Thread(new WaitRunnable());
        t1.start();
        t2.start();
        System.out.println("开始等待子线程运行完毕");
        t1.join();
        t2.join();
        System.out.println("所有线程都已执行完毕");
    }
}
复制代码
JAVA并发编程-Thread和Object类中的重要方法详解

如果注释掉两行join,主线程和子线程是并行执行的,没有等待子线程先执行完毕.

JAVA并发编程-Thread和Object类中的重要方法详解

2、主线程在等待子线程join的过程中,如果被中断,那么就不会再等待子线程执行完成了,而是catch到这个中断。

public class joinInterrupt {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mainThread.interrupt();
                    Thread.sleep(5000);
                    System.out.println("Thread1 finished.");
                } catch (InterruptedException e) {
                    System.out.println("子线程中断");
                }
            }
        });
        thread1.start();
        System.out.println("等待子线程运行完毕");
        try {
            thread1.join();
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+"主线程中断了");
            //thread1.interrupt();
        }
        System.out.println("子线程已运行完毕");
    }
}
复制代码

可以看出,主线程已经运行结束了,子线程才运行结束。这样就会导致主线程和子线程的不同步,如果要处理的话就应该使用 thread1.interrupt(); 让子线程也停止下来。

JAVA并发编程-Thread和Object类中的重要方法详解

3、在等待子线程join的过程中,主线程是什么状态(Waiting)

public class joinMainState {
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    //查看的是主线程的状态!
                    System.out.println(mainThread.getState());
                    System.out.println("Thread-0运行结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        System.out.println("等待子线程运行完毕");
        thread.join();
        System.out.println("子线程运行完毕");
    }
}
复制代码
原文  https://juejin.im/post/5eeba88ae51d4574010926a9
正文到此结束
Loading...