多线程(五) Thread和Object中线程相关方法
1.Object.wait() , wait(long timeout)
功能:让执行的当前线程休息一下,后面需要再唤醒。释放了该Object的monitor锁
使用条件:
- 线程拥有monitor锁才能用wait方法,执行后会放弃锁
- 必须在synchronize修饰的方法或代码块中
唤醒方法:
- 另一个线程调用这个对象的notify方法,恰好唤醒这个线程
- 另一个线程调用这个对象的notifyAll方法,就会唤醒所有等待线程
- 过了wait(long timeout) 时间,自动被唤醒了,如果设置的是0就永远等待
- 另一个线程调用这个线程的interrupt方法,就会抛出异常,释放monitor锁
2.Object.notify, notifyAll()
唤醒该Object上wait的线程, notify是随机唤醒一个,notifyAll唤醒所有。
使用条件:必须在synchronize修饰的方法或代码块中,拥有该monitor,才能执行notify
注意: 因为进行唤醒的方法notify执行完后,还持有monitor,所以被唤醒wait的线程不能立即获取锁,状态从waiting变成了blocked
代码示例1:
研究代码执行顺序,证明wait释放锁,notify不会释放锁,线程仍然持有锁进行下去
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 展示wait和notify的使用方法 * 1.研究代码执行顺序 * 2.证明wait释放锁 * 3.notify不会释放锁,线程仍然持有锁进行下去 * @author Administrator * */ public class WaitTest { static volatile Object object = new Object(); public static void main(String[] args) throws InterruptedException { Thread1 w = new Thread1(); Thread2 w2 = new Thread2(); Thread t1 = new Thread(w); Thread t2 = new Thread(w2); t1.start(); Thread.sleep(200); t2.start(); } static class Thread1 implements Runnable{ @Override public void run() { synchronized (object) { System.out.println(Thread.currentThread().getName()+"正在运行"); try { // 1.等待,并释放锁 object.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 5.唤醒后,等获取到锁执行 System.out.println(Thread.currentThread().getName()+"继续运行"); } } } static class Thread2 implements Runnable{ @Override public void run() { // 2.获取锁 synchronized (object) { System.out.println(Thread.currentThread().getName()+"唤醒正在运行"); // 3.唤醒object上wait的线程 object.notify(); // 4.继续执行 System.out.println(Thread.currentThread().getName()+"唤醒继续运行"); } } } } Thread-0正在运行 Thread-1唤醒正在运行 Thread-1唤醒继续运行 Thread-0继续运行
代码示例2:证明wait只释放当前的那把锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 证明wait只释放当前的那把锁 * @author Administrator * */ public class WaitNotifyReleaseOwnMonitor { private static volatile Object object1 = new Object(); private static volatile Object object2 = new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (object1) { System.out.println("Thread1 get object1 lock"); synchronized (object2) { System.out.println("Thread1 get object2 lock"); try { // 持有2把锁,这里只释放object1的锁,进入waiting停止执行 object1.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("Thread1 release object1 lock"); } System.out.println("Thread1 Object1 end"); } System.out.println("Thread1 run end"); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (object1) { System.out.println("Thread2 begin to notify"); // 持有object1的锁,并唤醒object1上wait的线程,现在不会释放锁得代码块执行完 object1.notify(); System.out.println("Thread2 end to notify"); } } }); t1.start(); Thread.sleep(100); t2.start(); } }
代码示例3:实现生产者消费者模式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 生产者消费者模式用wait,notify实现 * @author Administrator * */ public class ProducerConsumerModel { public static void main(String[] args) { EventStorage storage = new EventStorage(); Consumer consumer = new Consumer(storage); Producer producer = new Producer(storage); new Thread(consumer).start(); new Thread(producer).start(); } } // 仓库, 取和放商品交替进行 class EventStorage{ LinkedList<Date> list; int maxSize; public EventStorage() { list = new LinkedList<Date>(); maxSize = 10; } public synchronized void put() { // 仓库满了等待放 if(list.size() == maxSize) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } list.add(new Date()); System.out.println("仓库里有"+list.size()+"个商品"); notify(); } public synchronized void get() { // 仓库空了等待取 if(list.size() == 0) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("取出"+list.poll()+"商品,仓库中还有"+list.size()+"个商品"); notify(); } } class Consumer implements Runnable{ EventStorage storage; public Consumer(EventStorage storage) { this.storage = storage; } @Override public void run() { for(int i=0; i< 100; i++) { storage.get(); } } } class Producer implements Runnable{ EventStorage storage; public Producer(EventStorage storage) { this.storage = storage; } @Override public void run() { for(int i=0; i< 100; i++) { storage.put(); } } }
代码示例4:实现两个线程交替打印奇偶数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 两个线程交替打印奇偶数 * 1.拿到锁就唤醒另一个线程 * 2.打印完就等待释放锁 * @author Administrator * */ public class WaitNotifyPrintOddEvenSync { static int i = 0; public static void main(String[] args) { Object obj = new Object(); new Thread(new Runnable() { @Override public void run() { synchronized (obj) { while(i<100) { System.out.println(Thread.currentThread().getName()+":"+ (i++)); // 唤醒等待的另一个线程 obj.notify(); try { // 自己陷入等待 obj.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }).start(); new Thread(new Runnable() { @Override public void run() { synchronized (obj) { while(i<100) { System.out.println(Thread.currentThread().getName()+":"+ (i++)); obj.notify(); try { obj.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }).start(); } }
面试问题:
问:wait和notify为什么在synchronize修饰的同步代码块中,sleep不用?
答: wait和notify是两个线程的通信交流,如果不在同步代码块中,wait执行前可能切换到notify方法的线程,在notify方法执行完后再执行wait造成死锁永久等待。sleep是单个线程的睡眠,不和其他线程交流。
问:wait和notify为什么定义在Object中,sleep定义在Thread中?
答: 因为wait和notify涉及到锁的获取释放,锁是对象的。经常会有线程持有多个对象的锁来进行配合。
3.Thread.sleep(long timeout);
线程睡眠一段时间进入timedwaiting状态,再执行,不会释放monitor锁和Lock锁。休息的是正在执行这行代码的线程。如果被interrupt会抛出异常并清楚中断状态。
面试问题:
问: wait和sleep方法的异同?
同-都可以使线程阻塞进入waiting或timedwaiting状态; 都可以响应中断,并抛出异常并请出中断状态
异- wait需要在同步代码块中;wait可以不用参数永久等待,直到被中断或唤醒notify;wait会释放monitor锁,sleep不会释放锁;
wait,notify是Object方法, sleep是Thread方法
4.thread.join()
join会使这行代码所在的线程(主线程)阻塞,直至join方法归属的线程(子线程t1)销毁为止。
public class Join { public static void main(String[] args) { Thread main = Thread.currentThread(); Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start"); try { System.out.println(main.getName()+" "+main.getState()); Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" end"); } }); t1.start(); try { t1.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" end"); } }
join源码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
join方法是个同步方法,底层时wait实现,主线程执行时获取t1的锁,主线程一直wait阻塞,直到t1执行完run(),销毁前执行notifyall所有t1上等待的线程,主线程唤醒继续执行
所以不能写thread.wait(), 因为线程对象在退出时会执行notifyAll, 所以线程对象不适合作为锁对象, 执行wait操作.
5.Thread.yield()
让出CPU的资源,让出
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Yield { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { long begin = System.currentTimeMillis(); int count = 0; for(int i=0; i<10000000; i++) { Thread.yield(); count+=1; } long cost = System.currentTimeMillis() - begin; System.out.println("耗时" + cost); } }).start(); } }
时间不一定。不释放锁。