如果对一块共享数据,多个线程做不同的操作,有读有写,就有必要进行线程通讯处理,以便提高效率。
一、线程通讯:
1、线程通讯的概念:
线程通讯指的是多个线程通过消息传递实现相互牵制,相互调度,即线程间的相互作用。
2、Java线程通讯的实现:
Java代码中基于对共享数据进行“wait()、notify()、notifyAll()”来实现多个线程的通讯。
经典例子:生产者和消费者的问题。
1)、Warehouse.java
<span style="font-size:18px;">import java.util.ArrayList; import java.util.List; /** * 仓库类,用储存商品,联系生产者和消费者 * @author hanwen * */ public class Warehouse { //定义一个集合,用来存储生产的产品 private List<String> products=new ArrayList<String>(); /** * 生产者生产商品,存储到仓库里 */ public void addproduct(String stringname){ products.add(stringname);//想仓库里面添加产品 } /** * 消费者消费商品 */ public String getProduct(){ //判断仓库是不是为空 if(!products.isEmpty()){ //从仓库中取产品,返回给消费者 String stringname=products.remove(0); return stringname; }else{ System.out.println("仓库为空,请及时生产产品..."); return ""; } } /** * 获取库存余量 * * @return */ public int getSize(){ //返回库存余量 return products.size(); } }</span>
2)、CustomerThread.java
<span style="font-size:18px;">/** * 消费者线程类 * * @author Administrator * */ public class CustomerThread implements Runnable { // 属性 private Warehouse wh; // 仓库对象 private String name; // 消费者姓名 // 构造函数传递参数 public CustomerThread(Warehouse wh, String name) { super(); this.wh = wh; this.name = name; } // 消费产品 @Override public void run() { while (true) { synchronized (wh) { if (wh.getSize() == 0) { try { System.out.println("仓库为空,消费者" + name + "进入等待状态........"); wh.wait(); System.out.println("线程等待结束,重新启动"); } catch (InterruptedException e) { e.printStackTrace(); } } else if (wh.getSize() > 0) { // 取走商品, String stringname = wh.getProduct(); System.out.println("消费者" + name + "取走了产品" + stringname); } } try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } } } }</span>
3)、ProductThread.java
<span style="font-size:18px;">import java.util.Random; public class ProductThread implements Runnable { // 定义一个仓库对象 private Warehouse wh; // 有参数的构造方法 public ProductThread(Warehouse wh) { super(); this.wh = wh; } /** * 开始生产产品 */ @Override public void run() { while (true) { // 自动的随机产生产品 String stringname = "p" + (new Random()).nextInt(1000); synchronized (wh) { // 把产品放进仓库 wh.addproduct(stringname); // 提示信息 System.out.println("仓库里面已经存入商品" + stringname); //唤醒线程,让他继续执行 //wh.notify(); wh.notifyAll(); } try { // 睡一会 Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } } } }</span>
4)、TestDemo.java
<span style="font-size:18px;">/** * 测试类 * @author Administrator * */ public class TestDemo { public static void main(String[] args) { //实例化仓库对象 Warehouse wh=new Warehouse(); //实例化生产者对象 ProductThread pt=new ProductThread(wh); //实例化消费者对象 CustomerThread ct=new CustomerThread(wh,"小明"); CustomerThread ct1=new CustomerThread(wh,"小花"); CustomerThread ct2=new CustomerThread(wh,"小灰灰"); CustomerThread ct3=new CustomerThread(wh,"小飞飞"); //建线程类进行包装 Thread t=new Thread(pt); Thread t2=new Thread(ct); Thread t3=new Thread(ct1); Thread t4=new Thread(ct2); Thread t5=new Thread(ct3); //启动线程 t.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }</span>
3、wait()、notify()、notifyAll() 方法:
①、final void notify() 唤醒在此对象监视器上等待的单个线程
②、final void notifyAll() 唤醒在此对象监视器上等待的所有线程
③、final void wait() throws InterruptedException 导致当前的线程等待,直到其他线程调用此对象的 notify()
方法或 notifyAll() 方法
④、final void wait(long timeout) throws InterruptedException 导致当前的线程等待,直到其他线程调用此对
象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
⑤、final void wait(long timeout, int nanos) throws InterruptedException 导致当前的线程等待,直到其他
线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实
际时间量。
⑥、wait() 和 notify() 方法(包括上述的所有方法,下同) 都是 Object 类的最终方法,所以每个类默认都拥有该方 法。为确保 wait() 和 notify() 的功能,在执行之前必须要先获得互斥锁,即一定要和 synchronized 一起使用。
如 synchronized 的锁对象是 obj 的话,wait 和 notify 正确的使用方法是obj.wait()和obj.notify()。
4、sleep() 和 wait() 的区别:
sleep()
wait()
属性 Thread的静态方法 Object的方法
作用
让本线程进入睡眠状态 让本线程进入等待状态
是否释放同步锁 不会释放同步所 会释放同步所
5、线程死锁:
①、线程死锁指的两个线程互相持有对方依赖的共享资源,造成都无限阻塞。
②、导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。
③、解决死锁的方法:
让线程持有独立的资源。
尽量不采用嵌套的 synchronized 语句。
6、线程状态转换:
线程状态介绍
1)、新建状态(New):使用 new 关键字创建线程对象,仅仅被分配了内存;
2)、可运行状态(Runnable):线程具备获得CPU时间片的能力。线程进入可运行状态的情况如下:
①、线程 start() 方法被调用;
②、当前线程 sleep()、其它线程 join() 结束、等待用户输入完毕;
③、某个线程取得对象锁;
④、当前线程时间片用完了,调用当前线程的 yield() 方法。
3)、运行状态(Running):执行 run 方法,此时线程获得CPU的时间片;
4)、阻塞状态(Blocked):线程由于某些事件放弃CPU使用权,暂停运行。直到线程重新进入可运行
状态,才有机会转到运行状态。阻塞状态分为如下几种:
①、同步阻塞 – 线程获得同步锁,若被别的线程占用,线程放入锁池队列中。
②、等待阻塞 – 线程执行 wait() 方法,线程放入等待队列中。
③、其它阻塞 – 线程执行 sleep() 或 join() 或发出 I/O 请求。
5)、死亡状态(Dead):run、main() 方法执行结束,或者因异常退出了 run() 方法,线程进入死亡状
态,不可再次复生。