做多个网站 买vps,微信订阅号关键网站,千万不要做手游推广员,js网页设计大作业源代码使线程之间进行通信之后#xff0c;系统间的交互性更加强大#xff0c;在大大提高CPU利用率的同时还会使程序对各线程任务在处理的过程中进行有效的把控与监督。 1.不使用wait/notify实现线程间通信 使用sleep()while(true)也可以实现线程间通信。 例如:两个线程#xff0c… 使线程之间进行通信之后系统间的交互性更加强大在大大提高CPU利用率的同时还会使程序对各线程任务在处理的过程中进行有效的把控与监督。 1.不使用wait/notify实现线程间通信 使用sleep()while(true)也可以实现线程间通信。 例如:两个线程一个线程向集合中添加元素当集合中元素大小等于5的时候停止第二个线程 package cn.qlq.thread.six;import java.util.ArrayList;
import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** sleep()结合while(true)实现多个线程间通信* * author QiaoLiQiang* time 2018年12月10日下午9:32:52*/
public class Demo1 {private static final Logger LOGGER LoggerFactory.getLogger(Demo1.class);private static volatile List list new ArrayList();// 加volatile关键字是为了每次从主存中读取数据否则两个线程读取到的不一致public static void main(String[] args) {new Thread(new Runnable() {Overridepublic void run() {try {for (int i 0; i 10; i) {LOGGER.debug(add ele -{},threadName-{}, i, Thread.currentThread().getName());list.add(String.valueOf(i));Thread.sleep(1 * 1000);}} catch (InterruptedException e) {LOGGER.error(InterruptedException, e);}}}, A).start();new Thread(new Runnable() {Overridepublic void run() {try {while (true) {if (list.size() 5) {LOGGER.debug(list大小为5 了线程B要退出了);throw new InterruptedException();}}} catch (InterruptedException e) {LOGGER.error(抛出异常线程退出, e);}}}, B).start();}
} 结果: 21:45:05 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -0,threadName-A21:45:06 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -1,threadName-A21:45:07 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -2,threadName-A21:45:08 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -3,threadName-A21:45:09 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -4,threadName-A21:45:09 [cn.qlq.thread.six.Demo1]-[DEBUG] list大小为5 了线程B要退出了21:45:09 [cn.qlq.thread.six.Demo1]-[ERROR] 抛出异常线程退出java.lang.InterruptedException at cn.qlq.thread.six.Demo1$2.run(Demo1.java:43) at java.lang.Thread.run(Thread.java:745)21:45:10 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -5,threadName-A21:45:11 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -6,threadName-A21:45:12 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -7,threadName-A21:45:13 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -8,threadName-A21:45:14 [cn.qlq.thread.six.Demo1]-[DEBUG] add ele -9,threadName-A 虽然两个线程间实现了通信但有一个弊端就是线程B不停地通过while语句轮询机制来检测某一个条件这样会浪费CPU资源。 如果轮询的时间间隔很小更浪费CPU资源如果轮询的时间间隔很大有可能取不到想得到的数据。所以需要一种机制来实现减少CPU的资源而且还可以实现在多个线程间通信它就是wait/notify机制。 2. 等待/通知的实现 等待/通知在生活中的例子非常多比如在就餐时的一个例子:比如厨师炒好菜之后通知服务员上菜就是一个典型的等待通知的实现。 在前面几篇博客中多个线程也可以实现线程间通信原因就是多个线程共同访问同一个变量但那种通信机制不是等待/通知两个线程完全是主动地读取一个共享变量在花费读取时间的基础上读到的值并不是想要的并不能完全确定。所以等待/通知可以完美解决上述的问题。 方法wait()的作用是使当前执行代码的线程进行等待wait()是object类的方法该方法用来将当前线程置入预执行队列并且在wait()所在的代码行处进行停止直到接到通知或者被中断为止。在调用wait()之前线程必须获得该对象的对象级别锁即只能在同步方法或者同步代码块中调用wait()方法。在执行wait()方法后当前线程释放锁。在从wait()返回前线程与其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁则会抛出IllegalMonitorStateException它是RuntimeException的一个子类因此不需要try-catch语句进行捕获异常。 方法notify()也要在同步方法或者同步块中调用即调用前也必须获得该对象的对象级别锁。如果调用notify()时没有持有适当的锁则会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该对象的对象锁的其他线程如果有多个线程则有线程规划器随机挑选其中一个呈wait状态的线程对其发出notify并使它等待获取该对象的对象锁。需要说明的是在执行notify()之后,当前线程不会马上释放该对象锁呈wait状态的线程也不会马上获得该对象锁要等到nitofy()方法的线程将程序执行完也就是退出synchronized同步方法或者代码块释放锁之后。当第一个对象获得了该对象锁的wait线程继续运行完毕以后它会释放掉对象锁此时如果该对象没有再次使用notify()语句即便该对象已经空闲其他wait状态等待的线程由于没有得到该对象的通知还会继续阻塞在wait状态直到这个对象发出一个notify或notifyAll。 一句话总结:wait使线程停止运行释放锁而notify使线程继续运行但不会释放锁。两者都必须在获得同步锁才能使用。 如下:没有同步锁中调用wait()方法报错:(出现异常的原因是没有对象监视器也就是没有同步加锁) package cn.qlq.thread.six;/*** 不在同步中调用wait* * author Administrator**/
public class Demo2 {public static void main(String[] args) throws InterruptedException {Object object new Object();object.wait();}
} 结果: Exception in thread main java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:503) at cn.qlq.thread.six.Demo2.main(Demo2.java:6) 一个简单的wait/notify的例子: package cn.qlq.thread.six;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** wait/notify的基本使用* * author Administrator**/
public class Demo3 {private static final Logger LOGGER LoggerFactory.getLogger(Demo3.class);public static void main(String[] args) throws InterruptedException {final Object object new Object();Thread threadA new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {LOGGER.debug(进入同步代码块准备wait(),threadName-{}, Thread.currentThread().getName());Thread.sleep(1 * 1000);object.wait();LOGGER.debug(退出同步代码块wait()结束,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error);}}}}, A);threadA.start();// 开启一个线程占用锁之后唤醒一个线程Thread.sleep(1);Thread threadB new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {LOGGER.debug(进入同步代码块准备wait(),threadName-{}, Thread.currentThread().getName());Thread.sleep(1 * 1000);object.wait();LOGGER.debug(退出同步代码块wait()结束,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error);}}}}, B);threadB.start();// 开启一个线程占用锁之后唤醒一个线程Thread.sleep(1);Thread threadC new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {LOGGER.debug(进入同步代码块准备notify(),threadName-{}, Thread.currentThread().getName());Thread.sleep(1 * 1000);object.notify();// object.notifyAll();LOGGER.debug(退出同步代码块notify()结束,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}}}, C);threadC.start();Thread.sleep(5 * 1000);LOGGER.debug(A线程状态-{}, threadA.getState());LOGGER.debug(B线程状态-{}, threadB.getState());LOGGER.debug(C线程状态-{}, threadC.getState());}
} 结果:(A线程首先进入同步代码块然后通过wait之后释放锁;C占用锁然后发出notify通知;C执行完之后B获得同步锁B是正常的获得锁)然后进入wait释放锁;接下来A获得锁之后执行wait后面的代码执行完释放锁但是由于没有notify所以B仍然处于waiting状态。)--也证明发出notify()不会释放锁而是方法正常结束后才释放锁。 采用wait/notify实现上面的例子:两个线程一个线程向集合中添加元素当集合中元素大小等于5的时候停止第二个线程 package cn.qlq.thread.six;import java.util.ArrayList;
import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** wait/notify的实现两个线程通信* * author Administrator**/
public class Demo4 {private static final Logger LOGGER LoggerFactory.getLogger(Demo4.class);public static void main(String[] args) throws InterruptedException {final ListString list new ArrayListString();Thread threadA new Thread(new Runnable() {Overridepublic void run() {synchronized (list) {try {for (int i 0; i 10; i) {list.add(i );LOGGER.debug(add ele - {}, i);if (list.size() 5) {LOGGER.debug(wait---------------);list.wait();}}} catch (InterruptedException e) {LOGGER.error(InterruptedException error);}}}}, A);threadA.start();// 开启一个线程占用锁之后唤醒一个线程Thread.sleep(1);Thread threadB new Thread(new Runnable() {Overridepublic void run() {synchronized (list) {LOGGER.debug(run,threadName-{}, Thread.currentThread().getName());list.notify();LOGGER.debug(threadName-{}, notify, Thread.currentThread().getName());}}}, B);threadB.start();}
} 结果: 18:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 018:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 118:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 218:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 318:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 418:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] wait---------------18:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] run,threadName-B18:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] threadName-B, notify18:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 518:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 618:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 718:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 818:05:23 [cn.qlq.thread.six.Demo4]-[DEBUG] add ele - 9 关键字Synchronized可以将一个Object对象作为同步对象来看待而Java为每个Object都实现了wait()和notify()方法它们必须用在被synchronized同步的object的临界区内。通过调用wait()方法可以使处于临界区的线程进入等待状态同时释放同步对象的锁。而notify()操作可以唤醒一个因调用了wait操作而处于线程阻塞状态中的线程使其进入就绪状态。被重新唤醒的线程会试图重新获取临界区的控制权也就 是锁并继续执行临界区内wait之后的代码。如果发出notify操作时没有处于线程阻塞状态中的线程那么该命令会被忽略。 wait()方法可以使调用该方法的线程释放共享资源的锁然后从运行状态退出进入等待队列直到被再次唤醒。 notify()方法可以随机唤醒等待队列中等待同一共享资源的一个线程并使该线程退出等待队列进入可运行状态也就是notify()仅通知一个线程。 notifyAll()方法可以使所有正在等待队列中等待同一个共享资源的全部线程从等待状态退出进入可运行状态。此时优先级最高的那个线程最先执行但是也有可能是随机执行这取决于JVM虚拟机的实现。 每个锁对象都有两个队列一个是就绪队列一个是阻塞队列。就绪队列存储了将要获得锁的线程阻塞队列存储了被阻塞的线程。一个线程被唤醒后才会进入就绪队列等待CPU的调度反之一个线程被wait之后就进入阻塞队列等待下一次被唤醒。 3. 当interrupt方法遇到wait方法 当线程处于wait()状态时调用线程对象的interrupt()方法会出现InterruptedException对象。 package cn.qlq.thread.six;import java.util.ArrayList;
import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** wait遇到interrupt* * author Administrator**/
public class Demo5 {private static final Logger LOGGER LoggerFactory.getLogger(Demo5.class);public static void main(String[] args) throws InterruptedException {final ListString list new ArrayListString();Thread threadA new Thread(new Runnable() {Overridepublic void run() {synchronized (list) {try {for (int i 0; i 10; i) {list.add(i );LOGGER.debug(add ele - {}, i);if (list.size() 5) {LOGGER.debug(wait---------------);list.wait();}}} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, A);threadA.start();// 睡眠两秒钟发出中断信号Thread.sleep(2 * 1000);threadA.interrupt();}
} 结果: 21:35:00 [cn.qlq.thread.six.Demo5]-[DEBUG] add ele - 021:35:00 [cn.qlq.thread.six.Demo5]-[DEBUG] add ele - 121:35:00 [cn.qlq.thread.six.Demo5]-[DEBUG] add ele - 221:35:00 [cn.qlq.thread.six.Demo5]-[DEBUG] add ele - 321:35:00 [cn.qlq.thread.six.Demo5]-[DEBUG] add ele - 421:35:00 [cn.qlq.thread.six.Demo5]-[DEBUG] wait---------------21:35:02 [cn.qlq.thread.six.Demo5]-[ERROR] InterruptedException errorjava.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:503) at cn.qlq.thread.six.Demo5$1.run(Demo5.java:30) at java.lang.Thread.run(Thread.java:745) 4. 只唤醒一个线程 调用notify()一次只随机唤醒一个线程多次调用可以唤醒多个注意唤醒的是阻塞队列中被阻塞的线程。 package cn.qlq.thread.six;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** notify()每次唤醒一个线程* * author Administrator**/
public class Demo6 {private static final Logger LOGGER LoggerFactory.getLogger(Demo6.class);public static void main(String[] args) throws InterruptedException {final Object object new Object();Thread.sleep(1 * 1000);Thread threadA new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());object.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, A);threadA.start();Thread.sleep(1 * 1000);Thread threadB new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());object.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, B);threadB.start();Thread.sleep(1 * 1000);Thread threadC new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());object.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, C);threadC.start();Thread.sleep(1 * 1000);Thread threadD new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(notify start---------------,threadName-{}, Thread.currentThread().getName());object.notify();LOGGER.info(notify end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, D);threadD.start();Thread.sleep(10 * 1000);LOGGER.info(threadA state-{}, threadA.getState());LOGGER.info(threadB state-{}, threadB.getState());LOGGER.info(threadC state-{}, threadC.getState());LOGGER.info(threadD state-{}, threadD.getState());}
} 结果:(可以看到前面阻塞了ABC线程D线程随机唤醒了一个线程A) 21:53:16 [cn.qlq.thread.six.Demo6]-[INFO] wait start---------------,threadName-A21:53:18 [cn.qlq.thread.six.Demo6]-[INFO] wait start---------------,threadName-C21:53:20 [cn.qlq.thread.six.Demo6]-[INFO] wait start---------------,threadName-B21:53:22 [cn.qlq.thread.six.Demo6]-[INFO] notify start---------------,threadName-D21:53:22 [cn.qlq.thread.six.Demo6]-[INFO] notify end---------------,threadName-D21:53:22 [cn.qlq.thread.six.Demo6]-[INFO] wait end---------------,threadName-A21:53:27 [cn.qlq.thread.six.Demo6]-[INFO] threadA state-TERMINATED21:53:27 [cn.qlq.thread.six.Demo6]-[INFO] threadB state-WAITING21:53:27 [cn.qlq.thread.six.Demo6]-[INFO] threadC state-WAITING21:53:27 [cn.qlq.thread.six.Demo6]-[INFO] threadD state-TERMINATED 修改上面D线程的代码多次调用notify()可以看到会唤醒多个线程 Thread threadD new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(notify start---------------,threadName-{}, Thread.currentThread().getName());object.notify();LOGGER.info(notify end---------------,threadName-{}, Thread.currentThread().getName());LOGGER.info(notify start---------------,threadName-{}, Thread.currentThread().getName());object.notify();LOGGER.info(notify end---------------,threadName-{}, Thread.currentThread().getName());LOGGER.info(notify start---------------,threadName-{}, Thread.currentThread().getName());object.notify();LOGGER.info(notify end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, D);threadD.start(); 结果:(前面阻塞了3个线程调用了3次notify()就唤醒了三个线程) 21:56:11 [cn.qlq.thread.six.Demo6]-[INFO] wait start---------------,threadName-A21:56:13 [cn.qlq.thread.six.Demo6]-[INFO] wait start---------------,threadName-C21:56:15 [cn.qlq.thread.six.Demo6]-[INFO] wait start---------------,threadName-B21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] notify start---------------,threadName-D21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] notify end---------------,threadName-D21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] notify start---------------,threadName-D21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] notify end---------------,threadName-D21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] notify start---------------,threadName-D21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] notify end---------------,threadName-D21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] wait end---------------,threadName-A21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] wait end---------------,threadName-B21:56:17 [cn.qlq.thread.six.Demo6]-[INFO] wait end---------------,threadName-C21:56:22 [cn.qlq.thread.six.Demo6]-[INFO] threadA state-TERMINATED21:56:22 [cn.qlq.thread.six.Demo6]-[INFO] threadB state-TERMINATED21:56:22 [cn.qlq.thread.six.Demo6]-[INFO] threadC state-TERMINATED21:56:22 [cn.qlq.thread.six.Demo6]-[INFO] threadD state-TERMINATED 5. 唤醒所有的线程 notifyAll()可以唤醒对象阻塞队列中所有的线程知识唤醒的线程重新抢占锁的时候又是随机的抢占锁与notify()的区别是如果对象有多个阻塞线程需要多次调研notify()才能全部唤醒而调用notifyAll()一次可以唤醒全部阻塞队列的线程。 package cn.qlq.thread.six;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** notifyAll()唤醒全部线程* * author Administrator**/
public class Demo7 {private static final Logger LOGGER LoggerFactory.getLogger(Demo7.class);public static void main(String[] args) throws InterruptedException {final Object object new Object();Thread.sleep(1 * 1000);Thread threadA new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());object.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, A);threadA.start();Thread.sleep(1 * 1000);Thread threadB new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());object.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, B);threadB.start();Thread.sleep(1 * 1000);Thread threadC new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());object.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, C);threadC.start();Thread.sleep(1 * 1000);Thread threadD new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(2 * 1000);LOGGER.info(notifyAll start---------------,threadName-{}, Thread.currentThread().getName());object.notifyAll();LOGGER.info(notifyAll end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, D);threadD.start();Thread.sleep(10 * 1000);LOGGER.info(threadA state-{}, threadA.getState());LOGGER.info(threadB state-{}, threadB.getState());LOGGER.info(threadC state-{}, threadC.getState());LOGGER.info(threadD state-{}, threadD.getState());}
} 结果: 22:02:20 [cn.qlq.thread.six.Demo7]-[INFO] wait start---------------,threadName-A22:02:22 [cn.qlq.thread.six.Demo7]-[INFO] wait start---------------,threadName-C22:02:24 [cn.qlq.thread.six.Demo7]-[INFO] wait start---------------,threadName-B22:02:26 [cn.qlq.thread.six.Demo7]-[INFO] notifyAll start---------------,threadName-D22:02:26 [cn.qlq.thread.six.Demo7]-[INFO] notifyAll end---------------,threadName-D22:02:26 [cn.qlq.thread.six.Demo7]-[INFO] wait end---------------,threadName-B22:02:26 [cn.qlq.thread.six.Demo7]-[INFO] wait end---------------,threadName-C22:02:26 [cn.qlq.thread.six.Demo7]-[INFO] wait end---------------,threadName-A22:02:31 [cn.qlq.thread.six.Demo7]-[INFO] threadA state-TERMINATED22:02:31 [cn.qlq.thread.six.Demo7]-[INFO] threadB state-TERMINATED22:02:31 [cn.qlq.thread.six.Demo7]-[INFO] threadC state-TERMINATED22:02:31 [cn.qlq.thread.six.Demo7]-[INFO] threadD state-TERMINATED 6.方法 wait(long)使用---超过long时间自动唤醒 带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒如果超过这个时间则自动唤醒。 package cn.qlq.thread.six;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** wait(long)自动唤醒* * author Administrator**/
public class Demo8 {private static final Logger LOGGER LoggerFactory.getLogger(Demo8.class);public static void main(String[] args) throws InterruptedException {final Object object new Object();Thread threadA new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {Thread.sleep(5 * 1000);LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());// 等待10秒钟自动唤醒object.wait(5 * 1000);LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());Thread.sleep(5 * 1000);} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, A);threadA.start();Thread.sleep(2 * 1000);LOGGER.info(threadA state-{}, threadA.getState());Thread.sleep(5 * 1000);LOGGER.info(threadA state-{}, threadA.getState());Thread.sleep(5 * 1000);LOGGER.info(threadA state-{}, threadA.getState());Thread.sleep(5 * 1000);LOGGER.info(threadA state-{}, threadA.getState());}
} 结果: (线程调用sleep(long)和obj.wait(long)都是处于超时等待状态) 22:06:49 [cn.qlq.thread.six.Demo8]-[INFO] threadA state-TIMED_WAITING22:06:52 [cn.qlq.thread.six.Demo8]-[INFO] wait start---------------,threadName-A22:06:54 [cn.qlq.thread.six.Demo8]-[INFO] threadA state-TIMED_WAITING22:06:57 [cn.qlq.thread.six.Demo8]-[INFO] wait end---------------,threadName-A22:06:59 [cn.qlq.thread.six.Demo8]-[INFO] threadA state-TIMED_WAITING22:07:05 [cn.qlq.thread.six.Demo8]-[INFO] threadA state-TERMINATED 7. 通知过早 如果通知过早则会打乱程序正常的运行逻辑。 先发出通知此时对象的阻塞队列还没有阻塞的线程,所以相当于没有唤醒队列导致之后阻塞的线程一直处于阻塞状态 package cn.qlq.thread.six;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 通知过早打乱程序的运行逻辑* * author Administrator**/
public class Demo9 {private static final Logger LOGGER LoggerFactory.getLogger(Demo9.class);public static void main(String[] args) throws InterruptedException {final Object object new Object();Thread threadA new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {LOGGER.info(notify start---------------,threadName-{}, Thread.currentThread().getName());// 等待10秒钟自动唤醒object.notify();LOGGER.info(notify end---------------,threadName-{}, Thread.currentThread().getName());}}}, A);threadA.start();Thread.sleep(1 * 1000);Thread threadB new Thread(new Runnable() {Overridepublic void run() {synchronized (object) {try {LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());// 等待10秒钟自动唤醒object.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());} catch (InterruptedException e) {LOGGER.error(InterruptedException error, e);}}}}, B);threadB.start();}
} 结果(B线程会一直处于阻塞状态) 8. 等待wait的条件发生变化 在使用wait/notify的时候还需要注意另外一种情况也就是wait等待的条件发生了变化也容易造成程序的混乱。 package cn.qlq.thread.six;import java.util.ArrayList;
import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** wait条件发生变化导致程序混乱 (一个线程向集合中添加元素两个线程从集合中删除元素)* * author Administrator**/
public class Demo10 {private static final Logger LOGGER LoggerFactory.getLogger(Demo10.class);public static void main(String[] args) throws InterruptedException {final ListString list new ArrayListString();// 删除元素线程1Thread sub1 new Thread(new Runnable() {Overridepublic void run() {try {synchronized (list) {if (list.size() 0) {LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());// 等待10秒钟自动唤醒list.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());}LOGGER.info(list.remove -{}, threadName-{}, list.get(0), Thread.currentThread().getName());list.remove(0);}} catch (InterruptedException e) {e.printStackTrace();}}}, sub1);sub1.start();// 删除元素线程2Thread sub2 new Thread(new Runnable() {Overridepublic void run() {try {synchronized (list) {if (list.size() 0) {LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());// 等待10秒钟自动唤醒list.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());}LOGGER.info(list.remove -{}, threadName-{}, list.get(0), Thread.currentThread().getName());list.remove(0);}} catch (InterruptedException e) {e.printStackTrace();}}}, sub2);sub2.start();// 增加元素线程Thread.sleep(1 * 1000);Thread addThread new Thread(new Runnable() {Overridepublic void run() {synchronized (list) {list.add(1);LOGGER.info(添加元素-{},threadName-{}, 1, Thread.currentThread().getName());list.notifyAll();LOGGER.info(threadName-{} 执行list.notifyAll() 发出通知唤醒所有阻塞线程, Thread.currentThread().getName());}}}, B);addThread.start();}
} 结果: 22:34:58 [cn.qlq.thread.six.Demo10]-[INFO] wait start---------------,threadName-sub122:34:58 [cn.qlq.thread.six.Demo10]-[INFO] wait start---------------,threadName-sub222:34:59 [cn.qlq.thread.six.Demo10]-[INFO] 添加元素-1,threadName-B22:34:59 [cn.qlq.thread.six.Demo10]-[INFO] threadName-B 执行list.notifyAll() 发出通知唤醒所有阻塞线程22:34:59 [cn.qlq.thread.six.Demo10]-[INFO] wait end---------------,threadName-sub222:34:59 [cn.qlq.thread.six.Demo10]-[INFO] list.remove -1, threadName-sub222:34:59 [cn.qlq.thread.six.Demo10]-[INFO] wait end---------------,threadName-sub1Exception in thread sub1 java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:635) at java.util.ArrayList.get(ArrayList.java:411) at cn.qlq.thread.six.Demo10$1.run(Demo10.java:34) at java.lang.Thread.run(Thread.java:745) 报错原因如下:首先线程开始之后两个删除元素的线程处于阻塞状态而生产线程生产了一个元素之后就唤醒所有线程所以上面两个线程都被唤醒唤醒之后执行删除元素操作第一个线程能正常删除第二个线程删除会报越界异常的错误。 解决办法如下: package cn.qlq.thread.six;import java.util.ArrayList;
import java.util.List;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** wait条件发生变化导致程序混乱 (一个线程向集合中添加元素两个线程从集合中删除元素)* * author Administrator**/
public class Demo10 {private static final Logger LOGGER LoggerFactory.getLogger(Demo10.class);public static void main(String[] args) throws InterruptedException {final ListString list new ArrayListString();// 删除元素线程1Thread sub1 new Thread(new Runnable() {Overridepublic void run() {try {synchronized (list) {while (list.size() 0) {LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());// 等待10秒钟自动唤醒list.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());}LOGGER.info(list.remove -{}, threadName-{}, list.get(0), Thread.currentThread().getName());list.remove(0);}} catch (InterruptedException e) {e.printStackTrace();}}}, sub1);sub1.start();// 删除元素线程2Thread sub2 new Thread(new Runnable() {Overridepublic void run() {try {synchronized (list) {while (list.size() 0) {LOGGER.info(wait start---------------,threadName-{}, Thread.currentThread().getName());// 等待10秒钟自动唤醒list.wait();LOGGER.info(wait end---------------,threadName-{}, Thread.currentThread().getName());}LOGGER.info(list.remove -{}, threadName-{}, list.get(0), Thread.currentThread().getName());list.remove(0);}} catch (InterruptedException e) {e.printStackTrace();}}}, sub2);sub2.start();// 增加元素线程Thread.sleep(1 * 1000);Thread addThread new Thread(new Runnable() {Overridepublic void run() {synchronized (list) {list.add(1);LOGGER.info(添加元素-{},threadName-{}, 1, Thread.currentThread().getName());list.notifyAll();LOGGER.info(threadName-{} 执行list.notifyAll() 发出通知唤醒所有阻塞线程, Thread.currentThread().getName());}}}, B);addThread.start();}
} 结果: 22:40:06 [cn.qlq.thread.six.Demo10]-[INFO] wait start---------------,threadName-sub122:40:06 [cn.qlq.thread.six.Demo10]-[INFO] wait start---------------,threadName-sub222:40:07 [cn.qlq.thread.six.Demo10]-[INFO] 添加元素-1,threadName-B22:40:07 [cn.qlq.thread.six.Demo10]-[INFO] threadName-B 执行list.notifyAll() 发出通知唤醒所有阻塞线程22:40:07 [cn.qlq.thread.six.Demo10]-[INFO] wait end---------------,threadName-sub222:40:07 [cn.qlq.thread.six.Demo10]-[INFO] list.remove -1, threadName-sub222:40:07 [cn.qlq.thread.six.Demo10]-[INFO] wait end---------------,threadName-sub122:40:07 [cn.qlq.thread.six.Demo10]-[INFO] wait start---------------,threadName-sub1 9. 通知等待结合volatile实现一个多线程交替执行的例子 这个案例是为了复习wait/notify与volatile的知识点。创建20个线程实现交替打印☆和★符号也就是实现:下面的效果 ☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★ package cn.qlq.thread.six;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 交替打印特殊符号* 奇数线程打印☆偶数线程打印★* * author Administrator**/
public class Demo11 {private static final Logger LOGGER LoggerFactory.getLogger(Demo11.class);public volatile static boolean isOddThread true; // 标记是不是奇数线程public static void main(String[] args) throws InterruptedException {final Object lock new Object();//10个奇数线程打印☆for (int i 0; i 10; i) {new Thread(new Runnable() {Overridepublic void run() {try {synchronized (lock) {while (!isOddThread) {lock.wait();}LOGGER.info(ThreadName-{}, print -{},Thread.currentThread().getName(),☆);isOddThread false;lock.notifyAll();}} catch (InterruptedException e) {e.printStackTrace();}}}, odd i).start();}//10个偶数线程打印★for (int i 0; i 10; i) {new Thread(new Runnable() {Overridepublic void run() {try {synchronized (lock) {while (isOddThread) {lock.wait();}LOGGER.info(ThreadName-{}, print -{},Thread.currentThread().getName(),★);isOddThread true;lock.notifyAll();}} catch (InterruptedException e) {e.printStackTrace();}}}, even i).start();}}
} 结果: 13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd0, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even8, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd9, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even0, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd1, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even9, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd8, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even1, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd2, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even7, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd7, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even2, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd3, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even6, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd6, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even3, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd4, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even5, print -★13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-odd5, print -☆13:54:36 [cn.qlq.thread.six.Demo11]-[INFO] ThreadName-even4, print -★ 总结: wait()和notify()、wait(long)、notifyAll()都必须在获得资源锁的情况下使用而且获得哪个资源锁才能调用锁的wait()和notify()方法否则会抛出IllegalMonitorStateException异常。 每个锁对象都有两个队列一个是就绪队列一个是阻塞队列。就绪队列存储了将要获得锁的线程阻塞队列存储了被阻塞的线程。一个线程被唤醒后才会进入就绪队列等待CPU的调度反之一个线程被wait之后就进入阻塞队列等待下一次被唤醒。 wait()使线程进入waiting阻塞状态而且会立即释放锁并且暂停后面的代码。 wait(long)是超过long时间之后会自动解除阻塞(从阻塞队列转为就绪队列)。该方法会使线程进行超时等待状态与sleep(long)一样。 notify()每次唤醒一个线程是随机唤醒锁阻塞对象中的一个线程变为可运行状态重新强占CPU调度强占资源锁,而且notify()不会释放锁只有执行完之后才能释放锁 notifyAll()唤醒所有的阻塞线程使阻塞队列转为就绪队列然后从就绪队列中选取一个重新强占CPU调度强占资源锁,而且notify()不会释放锁只有执行完之后才能释放锁 转载于:https://www.cnblogs.com/qlqwjy/p/10099482.html