本文共 4334 字,大约阅读时间需要 14 分钟。
wait/notify/notifyAll案例运行过程:
1、生产者线程,在被wait()等待后,再次被唤醒时,是继续运行下一句代码,立即生产,并没有再次判断仓库是否已有产品。
2、无论生产者还是消费者,唤醒后,还要再次循环判断仓库是否为空,但是会引发死锁,因为所有线程都进入阻塞状态。
3、每次唤醒的时候,唤醒所有的线程解决这个问题。
说明:
1、wait() 当前线程放弃CPU执行权,并放弃锁,进入阻塞状态,直到被其他线程唤醒。
2、notify() 唤醒持有同一锁中调用wait的线程,被唤醒的线程是进入就绪状态等待CPU执行权。
3、notifyAll() 唤醒持有同一锁中调用wait的所有线程。
4、wait()、notify()、notifyAll() 这三个方法都是属于Object类,首先这些方法存在于同步代码方法/块中(可以理解为synchronized关键字修饰的同步方法或同步代码块中使用),而且使用这些方法时必须标识所属的同步的锁,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
5、wait()、notify()、notifyAll() 必须由锁对象调用。
6、wait()、notify()、notifyAll() 要在同步函数或同步代码块中调用,否则会报错InterruptedException异常。
7、调用wait()方法并不是立马释放锁,要等同步方法或同步代码块执行完成,同理调用notify()/notifyAll()方法会让等待锁中的线程从线程池进入等待锁池,在没有得到对象的锁之前,线程仍然无法获得CPU的调度和执行。
8、调用wait()方法进入睡眠状态的线程会进入线程池,直到代码运行结束,这个线程也跟着结束。
/** * 交替运行 */ public class J13WNThread { public static void main(String[] args) { Object object = new Object(); new A(object).start(); new B(object).start(); } } class A extends Thread {
private Object object;
public A (Object object){ this.object = object; }
@Override public void run() { test(); } public void test() { synchronized (object) { for (int i = 0; i < 100; i++) { // System.out.println("A - " + i); if(i % 2 == 0) { System.out.println("A 进入等待"); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // System.out.println("A 给唤醒了"); }else { System.out.println("A 唤醒 B"); object.notifyAll(); } } } } } class B extends Thread {
private Object object;
public B (Object object){ this.object = object; }
@Override public void run() { test(); } public void test() { synchronized (object) { for (int i = 0; i < 100; i++) { // System.out.println("B - " + i); if(i % 2 == 0) { System.out.println("B 唤醒 A"); object.notifyAll();
}else { System.out.println("B 进入等待"); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // System.out.println("B 给唤醒了"); } } } } } |
/** * wait/nofity/notifyAll模拟队列 */ public class J10ThreadMessageQueue { public static void main(String[] args) throws InterruptedException { Queue queue = new Queue(8);
//新增 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { try { queue.put(i); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();
Thread.sleep(10000);
//取走 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { try { queue.take(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } class Queue {
//容器 private LinkedList linkedList = new LinkedList<Object>();
//下限 private final int MIN_SIZE = 0;
//上限 private int maxSize;
//累计 private AtomicInteger size = new AtomicInteger();
//锁 private Object lock = new Object();
public Queue(int maxSize) { super(); this.maxSize = maxSize; }
public void put(Object object) throws InterruptedException { synchronized (lock) { //最大了,能添加要等待有人取走 while (maxSize == size.get()) { lock.wait(); } //添加元素、增加步长、唤醒另外一个线程 linkedList.add(object); size.incrementAndGet(); lock.notify(); System.out.println("新增元素:" + object); } }
public Object take() throws InterruptedException { synchronized (lock) { //没有元素,等待有人加入元素 while (MIN_SIZE == size.get()) { lock.wait(); } //删除元素、减少步长、唤醒另外一个线程 Object poll = linkedList.poll(); size.decrementAndGet(); lock.notify(); System.out.println("返回元素:" + poll); } return null; } } |
守护(后台)线程
在Java中线程分为两种,用户线程和守护(后台)线程,在运行线程时,默认是用户线程。
用户线程就是之前的所有例子,即便主线程结束了,子线程还是会继续运行,直到结束。
守护(后台)线程就是主线程结束了,守护(后台)线程也跟着结束,其中守护线程的优先级是比较低的,用于为系统中的其他对象和线程提供服务的,所以也称为服务线程。
在代码中可以通过Thread.setDaemon(true); 设置为守护(后台)线程,默认是为false 的,即用户线程。
垃圾回收线程就是典型的守护线程,始终以低级别的状态运行着,用于监控和管理系统中的可回收资源,当程序不再由任何运行的线程,程序就不会在产生垃圾,垃圾回收器也就无事可做了,所以当垃圾回收线程是JVM仅剩的线程时,垃圾回收线程会自动离开。
/** * 守护(后台)线程:在使用手机过程中,应用还是会自动下载更新包(守护线程),只要退出应用了(主线程退出),更新包也就不在下载了。 */ public class J15DaemonThread { public static void main(String[] args) { new QQ().start(); } } class QQ extends Thread { @Override public void run() { Download download = new Download(); download.setDaemon(true); download.start(); // download.setDaemon(true); //先设置为守护(后台)线程,再启动,否则会抛出IllegalThreadStateException异常 for (int i = 0; i < 100; i++) { System.out.println("运行QQ时长:" + i); } System.out.println("退出QQ。"); } } class Download extends Thread { @Override public void run() { System.out.println("开始下载。"); for (int i = 0; i < 100; i++) { System.out.println("下载 " + i + "%"); } System.out.println("完成。"); } } |
转载地址:http://qlmxi.baihongyu.com/