手机网站前端开发布局技巧,网站下载免费,凡客网站的域名怎么做,什么是电子商务公司1 概述 线程时操作系统中独立的个体#xff0c;但这些个体如果不经过特殊的处理是不能成为一个整体的。线程间的通信就是使线程成为整体的比用方案之一#xff0c;可以说#xff0c;是线程间进行通信后系统之间的交互性会更强大#xff0c;CPU利用率会得以大幅提高#xf…1 概述 线程时操作系统中独立的个体但这些个体如果不经过特殊的处理是不能成为一个整体的。线程间的通信就是使线程成为整体的比用方案之一可以说是线程间进行通信后系统之间的交互性会更强大CPU利用率会得以大幅提高同时程序员在处理的过程中可以有效把控与监督各线程任务。
2 不使用wait/notify机制进行通信的缺点
public class MyList {volatile private List list new ArrayList();public void add(){list.add(jay chou);}public int size(){return list.size();}} public class ThreadA extends Thread{private MyList list;public ThreadA(MyList list) {this.list list;}Overridepublic void run(){try {for (int i 0; i 10; i) {list.add();System.out.println(添加了 (i 1) 个元素);Thread.sleep(1000);}}catch (InterruptedException e){e.printStackTrace();}}
}
public class ThreadB extends Thread{private MyList list;public ThreadB(MyList list) {this.list list;}Overridepublic void run(){try {while(true){if(list.size() 5){System.out.println( 5了线程b要退出了);throw new InterruptedException();}}}catch (InterruptedException e){e.printStackTrace();}}
}
public class Run1 {public static void main(String[] args) {MyList list new MyList();ThreadA a new ThreadA(list);a.setName(a);a.start();ThreadB b new ThreadB(list);b.setName(b);b.start();}
} 虽然两个线程之间实现了通信但还存在缺点线程ThreadB不停地通过while语句轮询机制来检测某一个条件这样会浪费CPU资源。如果轮询的时间间隔很短更浪费CPU资源如果轮询的时间很长有可能会取不到想要得到的数据。
3 什么是wait/notify机制 wait/notify等待/通知机制在生活中很常见比如在就餐时就会出现如下图 厨师和服务员在”菜品传递台“上交互在这期间会想到几个问题 1、厨师做完一个菜的时间未知所以厨师把菜品放到”菜品传递台“上的时间也未知。 2、服务员取到菜的时间取决于厨师所以服务器就有”wait“的状态。 3、服务器如何能取到菜这又要取决于厨师。厨师将菜放到”菜品传递台“上其实就相当于一个notify这时服务员才可以拿到菜并交给就餐者。 4、这个过程中就出现了”wait/notify“机制。 需要说明的是前文中多个线程之间也可以实现通信就是就是多个线程共同访问同一个变量。但那种通信机制却不是”等待/通知“,两个线程完全是主动操作同一个共享变量。 但那种通信机制却不失“等待/通知”,两个线程完全是主动操作同一个共享变量在花费读取时间的基础上读到的值并不确定是不是想要的。
4 wait/notify机制的原理 注意拥有相同锁的线程才可以实现wait/notify机制。所以下文都是假定操作同一个锁。 wait()是Object类的方法它的作用是使当前执行wait()方法的线程进行等待在wait()所在的代码行处暂停执行并释放锁直到接到通知或被中断为止。在调用wait()之前线程必须要获得该对象的对象级别锁即只能在同步方法或同步块中调用wait()方法。通过通知机制是某个线程继续执行wait()方法后面的代码时对线程的选择是按照执行wait()方法的顺序确定的并需要重新获得锁。如果调用wait()时没有持有适当的锁则抛出IllegalMonitorStateException。它是RuntimeException的一个子类因此不需要try-catch语句进行捕获异常。 notify()也要在同步方法或同步块中调用即在调用前线程必须要获得锁如果调用notify()没有持有适当的锁也会抛出IllegalMonitorStateException。该方法用来通知哪些可能等待该锁的其他线程如果有多个线程等待则按照执行wait()方法的顺序对呈等待状态的线程发出1次通知并使那个线程重新获取锁。需要说明的是执行notify()方法后当前线程不会马上释放锁呈等待状态的线程也并不能马上获取该对象锁要等到执行notify()方法的线程将程序执行完也就是退出同步区域后当前线程才会释放锁而呈等待状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的等待线程运行完毕后它会释放掉该对象锁。此时如果没有再次使用notify语句那么其他等待状态的线程会因为没有得到通知而继续等待。 wait和notify是Object类中的方法。总结来说wait是线程暂停运行而notify通知暂停的线程继续运行。
5 wait()的基本用法 wait()的作用是使当前线程暂停运行并释放锁。
public class Test1 {public static void main(String[] args) {try {String s new String();s.wait();}catch (InterruptedException e){e.printStackTrace();}}
} 出现异常的原因是没有“对象监视器”,也就是没有锁。
public class Test2 {public static void main(String[] args) {try {String lock new String();System.out.println(synchronzied之前);synchronized (lock){System.out.println(进入synchronzied代码块);lock.wait();System.out.println(执行完wait()方法);}System.out.println(执行完synchronzied代码块的代码);}catch (InterruptedException e){e.printStackTrace();}}
}此时线程已经开始等待但不能永远等待下去否则程序就不会继续向下运行了。使用notify()方法可以是等待状态的线程继续运行。
6 实现wait/notify机制
public class MyThread1 extends Thread{private Object lock;public MyThread1(Object lock) {this.lock lock;}Overridepublic void run(){try {synchronized (lock){System.out.println(开始 等待时间 Utils.data(System.currentTimeMillis()));lock.wait();System.out.println(结束 等待时间 Utils.data(System.currentTimeMillis()));}}catch (InterruptedException e){e.printStackTrace();}}
}
public class MyThread2 extends Thread{private Object lock;public MyThread2(Object lock) {this.lock lock;}Overridepublic void run(){synchronized (lock){System.out.println(开始 通知时间 Utils.data(System.currentTimeMillis()));lock.notify();System.out.println(结束 通知时间 Utils.data(System.currentTimeMillis()));}}
}
public class Run1 {public static void main(String[] args) {try {Object object new Object();MyThread1 t1 new MyThread1(object);t1.start();Thread.sleep(3000);MyThread2 t2 new MyThread2(object);t2.start();}catch (InterruptedException e){e.printStackTrace();}}
} 从运行结果来看3秒后线程被通知唤醒。
7 使用wait/notify实现线程销毁 使用wait/notify可以在某个特定条件下销毁线程。
public class MyList {private static List list new ArrayList();public static void add(){list.add(1);}public static int size(){return list.size();}
}public class ThreadA extends Thread{private Object lock;public ThreadA(Object lock) {this.lock lock;}Overridepublic void run(){try {synchronized (lock){if(MyList.size() ! 5){System.out.println(开始等待 Utils.data(System.currentTimeMillis()));lock.wait();System.out.println(结束等待 Utils.data(System.currentTimeMillis()));}}}catch (InterruptedException e){e.printStackTrace();}}
}
public class ThreadB extends Thread{private Object lock;public ThreadB(Object lock) {this.lock lock;}Overridepublic 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(添加了 (i1));Thread.sleep(1000);}}}catch (InterruptedException e){e.printStackTrace();}}
}
public class Run1 {public static void main(String[] args) throws InterruptedException {Object lock new Object();ThreadA t1 new ThreadA(lock);t1.start();Thread.sleep(50);ThreadB t2 new ThreadB(lock);t2.start();}
}日志信息“结束等待”在最后输出这也说明notify()方法执行后并不立即释放锁这点后面会补充介绍。 关键字synchronzied可以将任何一个Object作为锁来看待而Java为每个Object都实现了wait()和notify()方法它们必须用在被同步的Object的临界区内。通过调用wait()方法可以使处于临界区内的线程进入等待状态同时释放被同步对象的锁而notify操作可以唤醒一个由于调用了wait操作而处于wait状态中的线程使其进入就绪状态被重新唤醒的线程会试图重新获取临界区的控制权也就是锁并继续执行临界区内wait之后的代码。如果发出notify操作时没有处于wait状态中的线程那么该命令就会被忽略。 notify()方法按照执行wait方法的顺序唤醒等待同一个锁的“一个”线程进入可运行状态。也就是notify()方法仅通知“一个”线程。而notifyAll()方法执行后会按照执行 wait()方法的倒序依次唤醒全部的线程。
8 对业务代码进行凤封装 前面的代码是在自定义的类中处理业务而业务代码要尽量放在Service类中进行处理。
public class MyList {volatile private List list new ArrayList();public void add(){list.add(q);}public int size(){return list.size();}
}public class MyService {private Object lock new Object();private MyList list new MyList();public void watiMethod(){try {synchronized (lock){if(list.size() ! 5){System.out.println(开始等待 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());lock.wait();System.out.println(结束等待 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());}}}catch (InterruptedException e){e.printStackTrace();}}public void notifyMethod(){try {synchronized (lock){System.out.println(开始通知 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());for (int i 0; i 10; i) {list.add();if(list.size() 5){lock.notify();System.out.println(发出通知wait后面的代码还没有立即执行因为锁没有释放);}System.out.println(add次数 (i1));Thread.sleep(1000);}System.out.println(结束通知 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());}}catch (InterruptedException e){e.printStackTrace();}}
}
public class ThreadA extends Thread{private MyService service;public ThreadA(MyService service) {this.service service;}Overridepublic void run(){service.watiMethod();}
}
public class ThreadB extends Thread{private MyService service;public ThreadB(MyService service) {this.service service;}Overridepublic void run(){service.notifyMethod();}
}public class Run1 {public static void main(String[] args) throws InterruptedException {MyService service new MyService();ThreadA t1 new ThreadA(service);t1.start();Thread.sleep(3000);ThreadB t2 new ThreadB(service);t2.start();}
} 9 线程状态的切换 前面介绍了与Thread有关的大部分API这些API可以改变线程对象的状态。 1创建了一个新的线程对象后再调用它的start方法系统会为此线程分配CPU资源处于可运行状态是一个准备运行的阶段。如果线程抢占到CPU资源此线程就处于运行状态。 2可运行状态和运行状态可互相切换因为有可能线程运行一段时间后其他高优先级的线程抢占了CPU资源这时此线程就从运行状态变成可运行状态。 线程进入可运行状态可分为以下四种情况 1、调用sleep()方法后经过的时间超过了指定的休眠时间 2、线程成功获得了试图同步的监视器 3、线程正在等待某个通知其他线程发出了通知 4、处于挂起状态的线程调用了resume方法。 3暂停状态结束后线程进入可运行状态等待系统重新分配资源。 出现阻塞的情况可分为以下5种 1、线程调用sleep方法主动放弃占用的处理器资源 2、线程调用了阻塞式I/O方法在该方法返回前该线程被阻塞 3、线程试图获得一个同步监视器但该监视器正被其他线程所持有 4、线程等待某个通知 5、程序调用了suspend方法将该线程挂起。此方法容易导致死锁应尽量避免使用。 4run方法运行结束后进入销毁阶段整个线程执行完毕。
10 wait()方法导致锁立即释放 wait()被执行后锁会被立即释放但执行完notify方法后锁不会立即释放。
public class Service {public void testMethod(Object lock){try {synchronized (lock){System.out.println(开始等待);lock.wait();System.out.println(结束等待);}}catch (InterruptedException e){e.printStackTrace();}}
}
public class ThreadA extends Thread{private Object lock;public ThreadA(Object lock) {this.lock lock;}Overridepublic void run(){Service service new Service();service.testMethod(lock);}
}
public class ThreadB extends Thread{private Object lock;public ThreadB(Object lock) {this.lock lock;}Overridepublic void run(){Service service new Service();service.testMethod(lock);}
}public class Run1 {public static void main(String[] args) {Object locl new Object();ThreadA t1 new ThreadA(locl);t1.start();ThreadB t2 new ThreadB(locl);t2.start();}
} 11 sleep()方法不释放锁 如果将wait方法改成sleep方法就成了同步效果。
public class Service {public void testMethod(Object lock){try {synchronized (lock){System.out.println(开始等待);Thread.sleep(4000);System.out.println(结束等待);}}catch (InterruptedException e){e.printStackTrace();}}
}12 notify方法不立即释放锁 还有一个结论要进行实验方法notify被执行后不立即释放锁。
public class MyService {private Object lock new Object();public void testMethod(){try {synchronized (lock){System.out.println(开始等待时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());lock.wait();System.out.println(结束等待时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());}}catch (InterruptedException e){e.printStackTrace();}}public void testNofity(){try {synchronized (lock){System.out.println(开始通知时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());lock.notify();Thread.sleep(4000);System.out.println(结束通知时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());}}catch (InterruptedException e){e.printStackTrace();}}
} public class ThreadA extends Thread{private MyService service;public ThreadA(MyService service) {this.service service;}Overridepublic void run(){service.testMethod();}
} public class ThreadB extends Thread{private MyService service;public ThreadB(MyService service) {this.service service;}Overridepublic void run(){service.testNofity();}
}public class Run1 {public static void main(String[] args) throws InterruptedException {MyService service new MyService();ThreadA t1 new ThreadA(service);t1.start();Thread.sleep(500);ThreadB t2 new ThreadB(service);t2.start();}
}通过控制台打印的时间来分析得到的结论必须执行完notify方法所在的同步代码块后才释放锁。
13 notify方法只通知一个线程 每次调用notify方法时只通知一个线程进行唤醒唤醒的顺序按执行wait方法的正序。
public class MyService {private Object lock new Object();public void waitMethod(){try {synchronized (lock){System.out.println(开始等待时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());lock.wait();System.out.println(结束等待时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());}}catch (InterruptedException e){e.printStackTrace();}}public void notifyMethod(){synchronized (lock){System.out.println(开始唤醒时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());lock.notify();System.out.println(结束唤醒时间 Utils.data(System.currentTimeMillis()) Thread.currentThread().getName());}}
}
public class ThreadA extends Thread{private MyService service;public ThreadA(MyService service) {this.service service;}Overridepublic void run(){service.waitMethod();}
}
public class ThreadB extends Thread{private MyService service;public ThreadB(MyService service) {this.service service;}Overridepublic void run(){service.notifyMethod();}
}public class Run1 {public static void main(String[] args) throws InterruptedException {MyService service new MyService();for (int i 0; i 10; i) {ThreadA t1 new ThreadA(service);t1.start();}Thread.sleep(1000);ThreadB t1 new ThreadB(service);t1.start();Thread.sleep(500);ThreadB t2 new ThreadB(service);t2.start();Thread.sleep(500);ThreadB t3 new ThreadB(service);t3.start();Thread.sleep(500);ThreadB t4 new ThreadB(service);t4.start();Thread.sleep(500);ThreadB t5 new ThreadB(service);t5.start();} 通过以上几个实验得出以下三个结论 1、执行完notify方法后按照执行wait的顺序唤醒其他线程。notify所在的同步到代码块执行完才会释放对象的锁其他线程继续执行wait之后的代码。 2、在执行同步代码块的过程中遇到异常而导致县城终止时锁也会被释放。 3、在执行同步代码块的过程中执行了锁所属对象的wait方法这个线程会释放对象锁等待被唤醒。
14 notifyAll方法通知所有线程 前面示例中通过多次调用notify方法来实现5个线程被唤醒当并不能保证实际系统中仅有5个线程就是notify方法的调用次数小于线程对象的数量那么会出现部分线程对象没有被唤醒的情况。为了唤醒全部线程可以使用notifyAll方法。 注意notifyAll方法会按照执行wait方法的倒序依次对其他线程进行唤醒。对上一节的MyService.java进行修改把notifyMethod()方法中的notify()改成notifyAll()然后再新建Run2.java类。
public class Run2 {public static void main(String[] args) throws InterruptedException {MyService service new MyService();for (int i 0; i 10; i) {ThreadA t1 new ThreadA(service);t1.start();}Thread.sleep(1000);ThreadB t2 new ThreadB(service);t2.start();}
} 通过运行结果看到唤醒的顺序是调用wait()方法的倒序。