线程间怎么通信?线程间通信方式有哪些?
2 个回答
想多了全是问题,想开了全是答案。楚灵线程间通信方式主要包括以下几种: 1. **共享变量** - 多个线程通过访问共享内存中的变量进行数据交换。 - 常用方式:`volatile`关键字、锁机制(如`synchronized`、`Lock`)、原子类(如`AtomicInteger`)等。 2. **等待/通知机制(wait/notify)** - 通过`Object.wait()`使线程等待,`Object.notify()`或`Object.notifyAll()`唤醒等待的线程。 - 常用于生产者-消费者模式。 3. **Condition接口** - 配合`ReentrantLock`使用,实现比`wait/notify`更灵活的等待通知机制。 - 典型用法:`await()`、`signal()`、`signalAll()`。 4. **管道通信(Pipe)** - 使用Java IO提供的管道流(如`PipedInputStream`、`PipedOutputStream`)在线程之间传输数据。 5. **阻塞队列(BlockingQueue)** - 如`ArrayBlockingQueue`、`LinkedBlockingQueue`,适合用于线程池、生产者-消费者场景。 6. **Thread.join()** - 主线程等待子线程结束后再继续执行,实现简单的线程间等待通信。 7. **CountDownLatch/CyclicBarrier** - `CountDownLatch`:多个线程等待某个线程完成动作后,再继续执行。 - `CyclicBarrier`:多个线程相互等待,所有线程达到某个点后再继续执行。 8. **Semaphore(信号量)** - 控制线程访问资源的数量,线程间可借助信号量协调动作。 9. **Exchanger** - 用于两个线程间交换数据。发布于 2024-01-31 18:35 •IP属地 辽宁举报评论收藏
入体冰凉的月光,都比不上你绝情的目光。松林#### 1.synchronized锁 通过synchronized锁来进行同步,让一次只能一个线程来执行。 #### 2.等待/通知机制 ```java //假设我们的需求是B执行结束后A才能执行 //线程A的代码 synchronized(对象) { while(条件不满足) { while(条件不满足) { 对象.wait(); //线程A进行等待 } //线程A执行相关的的逻辑 } //线程B的代码 synchronized(对象) { //线程B执行相关的的逻辑 //线程B唤醒线程A 对象.notifyAll(); } ``` 等待/通知机制,是指一个线程A调用了对象objectA的wait()方法进入等待状态,而另一个线程B调用了对象objectA的notify()或者notifyAll()方法,线程A收到通知后从对象objectA的wait()方法返回,进而执行后续操作。上述两个线程通过对象objectA来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。  1)使用wait()、notify()和notifyAll()时需要先对调用对象加锁。 2)调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的 等待队列。 3)notify()或notifyAll()方法调用后,等待线程依旧不会从wait()返回,因为等待线程只是从等待队列到了同步队列,需要调用notify()或 notifAll()的线程释放锁之后,等待线程获得锁,才能从同步队列中移除,才有机会从wait()返回,才能继续往下执行。 4)notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll() 方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为 BLOCKED。 5)从wait()方法返回的前提是获得了调用对象的锁。  #### 3.管道 管道输入/输出流 管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要 用于线程之间的数据传输,而传输的媒介为内存。 管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、PipedInputStream、 PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。 PipedReader和PipedWriter可以一个线程A调用PipedWriter实例的write()方法,往里面写数据,然后与PipedWriter实例建立连接的PipedReader实例可以读到数据,线程B可以通过PipedReader实例读到数据。 在代码清单4-12所示的例子中,创建了printThread,它用来接受main线程的输入,任何 main线程的输入均通过PipedWriter写入,而printThread在另一端通过PipedReader将内容读出并打印。 代码清单4-12 Piped.java ```java public class Piped { public static void main(String[] args) throws Exception { PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(); // 将输出流和输入流进行连接,否则在使用时会抛出IOException out.connect(in); Thread printThread = new Thread(new Print(in), "PrintThread"); printThread.start(); int receive = 0; try { while ((receive = System.in.read()) != -1) { out.write(receive); } } finally { out.close(); } } static class Print implements Runnable { private PipedReader in; public Print(PipedReader in) { this.in = in; } public void run() { int receive = 0; try { while ((receive = in.read()) != -1) { System.out.print((char) receive); } } catch (IOException ex) { } } } } ``` #### 4.Thread.join Thread.join()的使用如果一个线程A执行了thread.join()语句,当前线程A会一直等待thread线程终止之后才从thread.join()返回,向下执行。线程Thread除了提供join()方法之外,还提供了join(long millis)和join(long millis,int nanos)两个具备超时参数的方法。 #### 5.ThreadLocal的使用 ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这 个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个 线程上的一个值。 ```java public class Profiler { // 第一次get()方法调用时会进行初始化(如果set方法没有调用),每个线程会调用一次 private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>() { protected Long initialValue() { return System.currentTimeMillis();} }; public static final void begin() { TIME_THREADLOCAL.set(System.currentTimeMillis()); } public static final long end() { return System.currentTimeMillis() - TIME_THREADLOCAL.get(); } public static void main(String[] args) throws Exception { Profiler.begin(); TimeUnit.SECONDS.sleep(1); System.out.println("Cost: " + Profiler.end() + " mills"); } } ``` Profiler可以被复用在方法调用耗时统计的功能上,在方法的入口前执行begin()方法,在 方法调用后执行end()方法,好处是两个方法的调用不用在一个方法或者类中,比如在AOP(面 向方面编程)中,可以在方法调用前的切入点执行begin()方法,而在方法调用后的切入点执行 end()方法,这样依旧可以获得方法的执行耗时。发布于 2022-02-06 17:47 •IP属地 云南举报评论收藏
公众号进入网站体验更佳

