《Java多线程编程核心技术》学习笔记(二)——多线程间通信
来源:互联网 发布:软件测试团队 编辑:程序博客网 时间:2024/06/05 05:08
线程间通信
等待/通知机制
方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入”预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。在调用wait()之前,线程必须获取该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁,如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException
方法notify()也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁,如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。需要说明的是,在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll。
wait使线程停止运行,而notify使停止的线程继续运行
synchronized(lock){ System.out.println("开始 wait time="+System.currentTimeMillis()); lock.wait(); System.out.println("结束 wait time="+System.currentTimeMillis()); } ... synchronized(lock){ System.out.println("开始 notify time="+System.currentTimeMillis()); lock.notify(); System.out.println("结束 notify time="+System.currentTimeMillis()); }
释放锁的情况:
- 1)执行完同步代码块就会释放对象的锁
- 2)在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放
- 3)在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒
方法wait(long)的使用
带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒
生产者/消费者模式实现
//生产者 public class P{ private String lock; public P(String lock){ super(); this.lock = lock; } public void setValue(){ try{ synchronized(lock){ while(!ValueObject.value.equals("")){ System.out.println("生产者"+Thread.currentThread().getName+" WAITING了"); lock.wait(); } System.out.println("生产者"+Thread.currentThread().getName()+" RUNNABLE了"); String value = System.currentTimeMillis()+"_"+System.nanoTime(); ValueObject.value = value; lock.notify(); } }catch(InterruptedException e){ e.printStackTrace(); } } } //消费者 public class C{ private String lock; public C(String lock){ super(); this.lock = lock; } public void getValue(){ try{ synchronized(lock){ while(ValueObject.value.equals("")){ System.out.println("消费者"+Thread.currentThread().getName+" WAITING了"); lock.wait(); } System.out.println("消费者"+Thread.currentThread().getName()+" RUNNABLE"); ValueObject.value = ""; lock.notify(); } }catch(InterruptedException e){ e.printStackTrace(); } } } //线程P public class ThreadP extends Thread{ private P p; public ThreadP(P p){ super(); this.p = p; } @Override public void run(){ while(true){ p.setValue(); } } } //线程C public class ThreadC extends Thread{ private C c; public ThreadP(C c){ super(); this.c = c; } @Override public void run(){ while(true){ c.setValue(); } } } //执行 public class Run{ public static void main(String[] args) throws InterruptedException{ String lock = new String(""); P p = new P(lock); C c = new C(lock); ThreadP[] pThread = new ThreadP[2]; ThreadC[] cThread = new ThreadC[2]; for(int i = 0;i<2;i++){ pThread[i] = new ThreadP(p); pThread[i].setName("生产者"+(i+1)); cThread[i] = new ThreadC(c); pThread[i].setName("消费者"+(i+1)); pThread[i].start(); cThread[i].start(); } Thread.sleep(5000); Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()]; Thread.currentThread().getThreadGroup().enumerate(threadArray); for(int i =0;i<threadArray.length;i++){ System.out.println(threadArray[i].getName()+""+threadArray[i].getState()); } } }
多生产与多消费导致假死:notify唤醒的不是异类而是同类,比如”生产者”唤醒”生产者”或”消费者”唤醒”消费者”,就会导致所有的线程都不能继续运行下去,都呈WAITING状态
解决:用notifyAll()代替notify()
通过管道进行线程间通信:字节流
管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输出管道中读取数据。从而实现不同线程间的通信
在Java的JDK中提供了4个类来使线程间可以进行通信:
- 1)PipedInputStream和PipedOutputStream
2)PipedReader和PipedWriter
//WriteData类public class WriteData{ public void writeMethod(PipedOutputStream out){ try{ System.out.println("write :"); for(int i = 0;i<300;i++){ String outData = ""+(i+1); out.write(outData.getBytes()); System.out.print(outData); } System.out.println(); out.close(); }catch(IOException e){ e.printStackTrace(); } }}//ReadData类public class ReadData{ public void readMethod(PipedInputStream input){ try{ System.out.println("read :"); byte[] byteArray = new byte[20]; int readLength = input.read(byteArray); while(readLength != -1){ String newData = new String(byteArray,0,readLength); System.out.print(newData); readLength = input.read(byteArray); } System.out.println(); input.close(); }catch(IOException e){ e.printStackTrace(); } }}//Write线程public class ThreadWrite extends Thread{ private WriteData write; private PipedOutputStream out; public ThreadWrite(WriteData write,PipedOutputStream out){ super(); this.write = write; this.out = out; } @Override public void run(){ write.writeMethod(out); }}//Read线程public class ThreadRead extends Thread{ private ReadData read; private PipedInputStream input; public ThreadRead(ReadData read,PipedInputStream input){ super(); this.read = read; this.input = input; } @Override public void run(){ read.readMethod(input); }}//Run类public class Run{ public static void main(String[] args){ try{ WriteData writeData = new WriteData(); ReadData readData = new ReadData(); PipedInputStream inputStream = new PipedInputStream(); PipedOutputStream outputStream = new PipedOutputStream(); //inputStream.connect(outputStream); outputStream.connnect(inputStream); ThreadRead threadRead = new ThreadRead(readData,inputStream); threadRead.start(); Thread.sleep(2000); ThreadWrite threadWrite = new THreadWrite(writeData,outputStream); threadWrite.start(); }catch(IOException e){ e.printStackTrace(); }catch(InterruptedException e){ e.printStackTrace(); } }}
使用inputStream.connect(outputStream)或outputStream.connect(inputStream)的作用使两个Stream之间产生通信链接,这样才可以将数据进行输出与输入
管道中还可以传递字符流,只要将PipedInputStream换成PipedReader,PipedOutputStream换成PipedWriter
多线程join的使用
在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。如果主线程想等到子线程执行完成后再结束,就要用到join()方法,
join()方法的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码
方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是”对象监视器”原理做为同步
在join过程中,如果当前线程对象被中断,则当前线程出现异常(join()和interrupt()彼此遇到就会出现异常)
join(long)中的参数是设定等待的时间
join(long)与sleep(long)的区别:
join(long)的功能在内部是使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。
而Thread.sleep(long)方法却不释放锁
类ThreadLocal的使用
变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static变量,如果想实现每一个线程都有自己的共享变量可以使用类ThreadLocal
类ThreadLocal主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据
public class ThreadLocalExt extends ThreadLocal{ //覆盖initialValue()方法具有初始值 @Override protected Object initialValue(){ return new Date().getTime(); } } public class Tools{ public static ThreadLocalExt t1 = new ThreadLocalExt(); } //线程A public class ThreadA extends Thread{ @Override public void run(){ try{ for(int i = 0;i<10;i++){ System.out.println("在ThreadA线程中取值="+Tools.t1.get()); Thread.sleep(100); } }catch(InterruptedException e){ e.printStackTrace(); } } } //Run类 public class Run(){ public static void main(String[] args){ try{ for(int i = 0;i<10;i++){ System.out.print(" 在Main线程中取值="+Tools.t1.get()); Thread.sleep(100); } Thread.sleep(5000); ThreadA a = new ThreadA(); a.start(); }catch(InterruptedException e){ e.printStackTrace(); } } }
- 《Java多线程编程核心技术》学习笔记(二)——多线程间通信
- Java多线程编程核心技术---线程间通信(二)
- Java多线程编程核心技术学习(二)
- 《Java多线程编程核心技术》学习笔记(一)——Java的多线程
- 【java多线程编程核心技术】3.线程间通信 -笔记总结
- 《java多线程编程核心技术》核心笔记(二)
- 《Java多线程编程核心技术》学习笔记-第一章
- Java多线程编程核心技术---线程间通信(一)
- 《Java多线程编程核心技术》学习笔记(三)——Lock、Timer、单例模式
- 学习笔记:Java 多线程编程核心技术(第一章)
- Java多线程编程核心技术(第一章笔记)
- Java多线程编程核心技术笔记
- 《Java多线程编程核心技术》笔记
- 《Java多线程编程核心技术》-笔记
- Java多线程编程核心技术学习(一)
- java多线程核心技术学习笔记
- 《java多线程编程核心技术》之线程间通信
- 《java多线程编程核心技术》读书笔记3:线程间的通信
- (第十六周项目2)大数据集上排序算法性能的体验
- systemctl 命令完全指南
- 第16周项目1 验证算法(6)堆排序
- IDEA的使用
- win10安装 svn遇到2503错误的解决办法
- 《Java多线程编程核心技术》学习笔记(二)——多线程间通信
- linux压缩和解压缩命令大全
- ImageView、TextView设置背景色三种方式
- 第15周项目1-验证算法
- 第十六周项目1-(4)验证算法快速排序
- apt-get常见错误
- 大数据组件安装准备工作1--在Virtualbox中安装Linux虚拟机
- local storage 存储 json 和数组
- Add Two Numbers