-
线程间通信wait、notify、notifyAll
-
何时使用:在多线程环境下,有时候一个线程的执行,依赖于另外一个线程的某种状态的改变,这个时候就可以使用wait、notify、notifyAll
-
wait和sleep的区别:
wait会释放持有的锁,而sleep不会,sleep只是让线程在指定的时间内,不去抢占cpu的资源
-
wait、notify必须放在同步代码块中,且必须拥有当前对象的锁,即不能获得A对象的锁,而调用B对象的wait,哪个对象wait,就得调用哪个对象的notify。
-
notify和notifyAll的区别:notify随机获取一个等待的线程,notifyAll唤醒所有在等待的线程。
-
示例代码
public class Demo1 { private static volatile boolean flag = false; public static void main(String[] args) throws InterruptedException { Object obj = new Object(); new Thread(() -> { while (!flag){ synchronized (obj){ try { System.out.println("flag is false"); obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("flag is true"); }).start(); Thread.sleep(1000L); new Thread(() -> { flag = true; synchronized (obj){ obj.notify(); } }).start(); } }
-
等待通知经典模型–生产者消费者
-
管道通信:使用管道流进行通信,一内存为媒介,用于线程之间的数据传输。主要用面向字节(PipedOutputStream, PipedInputStream),面向字符(PipedReader、PipedWriter)。
-
代码示例(将主线程获得的数据传输给其他的线程):
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PipedInputStream; import java.util.stream.Collectors; public class Reader implements Runnable { private PipedInputStream pipedInputStream; public Reader (PipedInputStream pipedInputStream){ this.pipedInputStream = pipedInputStream; } @Override public void run() { if(null != pipedInputStream){ String collect = new BufferedReader(new InputStreamReader(pipedInputStream)).lines().collect(Collectors.joining("\n")); System.out.println(Thread.currentThread().getName() + "----" + collect); } try { pipedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
import java.io.*; public class Main { public static void main(String[] args) throws IOException { PipedInputStream inputStream = new PipedInputStream(); PipedOutputStream outputStream = new PipedOutputStream(); outputStream.connect(inputStream); new Thread(new Reader(inputStream)).start(); BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new InputStreamReader(System.in)); outputStream.write(bufferedReader.readLine().getBytes()); } finally { if(null != bufferedReader){ bufferedReader.close(); } outputStream.close(); } } }
-
Thread.join通信(将两个线程合并):
-
使用场景:线程A执行到一半,需要一个数据,这个数据需要线程B去执行修改,只有B修改完成之后,A才能继续操作。
-
线程A的run方法里面,调用线程B的join方法,这个时候,线程A会等待线程B运行完成后,再接着运行线程A。
-
示例代码:
public class Main { public static void main(String[] args) { Thread thread = new Thread(() -> { System.out.println("线程1-----"+Thread.currentThread().getName()); try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程1+++++"+Thread.currentThread().getName()); }, "线程1"); new Thread(() -> { System.out.println("线程2-----"+Thread.currentThread().getName()); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程2+++++"+Thread.currentThread().getName()); }, "线程2").start(); } }
-
实现的机制:调用wait方法,知道其他线程执行完成之后,再继续执行。
-
ThreadLocal: 线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构,为每个线程单独存放一份变量副本,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
只要一个线程处于活动状态并且ThreadLocal实例可以访问,那么每个线程都拥有对其本地线程副本的隐式引用变量,一个线程消失后,它的所有副本线程局部实例受垃圾回收(除非存在其他对这些副本的引用)。
-
一般用的较多的是
ThreadLocal.get ThreadLocal.set ThreadLocal.remove ThreadLocal.initialValue
ThreadLocal没有被当前线程赋值或当前线程调用remove后再调用get方法,返回此方法值。
-
示例代码
/** * ThreadLocal使用示例 */ public class ThreadLocalDemo { ThreadLocal<Integer> num = ThreadLocal.withInitial(() -> 0); /** * 自增输出 */ public void inCreate(){ Integer myNum = num.get(); myNum++; System.out.println(Thread.currentThread().getName() + "=====" +myNum); num.set(myNum); } public static void main(String[] args) { ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo(); for (int i = 1; i < 3; i++){ int finalI = i; new Thread(() -> { while (true){ threadLocalDemo.inCreate(); try { Thread.sleep(finalI * 1000L); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } }
-
Condition: 可以在一个锁里面存在多种等待状态。可以替换wait、notify的方法
-
主要的方法
await wait signal notify signalAll notifyAll
线程间通信
最新推荐文章于 2024-01-12 18:59:05 发布