Java线程之间如何通信的,有哪些方式?

不念不忘少年蓝@ 2024-03-16 10:37 133阅读 0赞

" class="reference-link">199c034c0dbe47b8b9f3e6e442ecabfa.png

线程之间的通信方式主要有以下几种:

  1. 共享变量:线程之间可以通过共享变量来进行通信。不同的线程可以共享同一个变量,并在变量上进行读写操作。需要注意的是,共享变量可能会引发线程安全问题,需要通过同步机制来确保线程安全。
  2. 锁机制:锁机制是一种常用的线程同步机制,可以保证在同一时间只有一个线程能够访问共享资源。Java提供了多种锁类型,如 synchronized 关键字、ReentrantLock 类等。
  3. 条件变量:条件变量是一种线程间通信机制,它用于在一个共享资源上等待某个条件的成立。Java 提供了 Condition 接口来支持条件变量的实现,在使用 Condition 时需要先获取锁,然后调用 await() 方法等待条件成立,当条件成立时可以通过 signal() 或 signalAll() 方法唤醒等待该条件的线程。
  4. 信号量:信号量是一种常见的线程同步机制,可用于控制多个线程对共享资源的访问。Java 提供了 Semaphore 类来实现信号量,Semaphore 类有两个常用的方法 acquire() 和 release(),分别用于获取和释放信号量。
  5. 管道:管道是一种用于线程间通信的高级机制,它可以实现一个线程向另一个线程传送数据。Java 提供了 PipedInputStream 和 PipedOutputStream 两个类来支持管道的实现,其中 PipedInputStream 用于读取数据,PipedOutputStream 用于写入数据。

需要注意的是,以上通信方式都需要在多线程程序中谨慎使用,需要考虑线程安全和性能等方面的问题。为了确保程序正确、高效地运行,需要根据具体情况选择合适的线程通信方式,并进行相应的测试和优化。

具体的示例

共享变量

  1. public class SharedData {
  2. private int value;
  3. public synchronized int getValue() {
  4. return value;
  5. }
  6. public synchronized void setValue(int value) {
  7. this.value = value;
  8. }
  9. }

在这个示例中,定义了一个共享数据类 SharedData,其中包含一个整型变量 value 和两个同步方法 getValue()setValue(),用于获取和设置变量的值。由于这两个方法都是同步的,因此多个线程可以安全地访问该变量。

  1. public class SharedDataExample {
  2. public static void main(String[] args) throws InterruptedException {
  3. SharedData sharedData = new SharedData();
  4. Thread thread1 = new Thread(() -> {
  5. for (int i = 0; i < 10; i++) {
  6. sharedData.setValue(i);
  7. System.out.println(Thread.currentThread().getName() + " write " + sharedData.getValue());
  8. try {
  9. Thread.sleep(100);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. });
  15. Thread thread2 = new Thread(() -> {
  16. for (int i = 0; i < 10; i++) {
  17. System.out.println(Thread.currentThread().getName() + " read " + sharedData.getValue());
  18. try {
  19. Thread.sleep(100);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. });
  25. thread1.start();
  26. thread2.start();
  27. thread1.join();
  28. thread2.join();
  29. }
  30. }

在这个示例中,创建了两个线程分别用于读写共享数据 SharedData,多次执行该示例可以看到控制台输出表明两个线程在安全地访问共享变量。

锁机制

  1. public class LockExample {
  2. private static Lock lock = new ReentrantLock();
  3. private static int count = 0;
  4. private static void increase() {
  5. lock.lock();
  6. try {
  7. count++;
  8. } finally {
  9. lock.unlock();
  10. }
  11. }
  12. public static void main(String[] args) throws InterruptedException {
  13. Thread thread1 = new Thread(() -> {
  14. for (int i = 0; i < 10000; i++) {
  15. increase();
  16. }
  17. });
  18. Thread thread2 = new Thread(() -> {
  19. for (int i = 0; i < 10000; i++) {
  20. increase();
  21. }
  22. });
  23. thread1.start();
  24. thread2.start();
  25. thread1.join();
  26. thread2.join();
  27. System.out.println(count);
  28. }
  29. }

在这个示例中,使用了 Lock 接口和 ReentrantLock 类来对计数器进行同步,多次执行该示例可以看到最终输出的计数器值为 20000。

条件变量

  1. public class ConditionExample {
  2. private static Lock lock = new ReentrantLock();
  3. private static Condition condition = lock.newCondition();
  4. private static int count = 0;
  5. private static void await() throws InterruptedException {
  6. lock.lock();
  7. try {
  8. condition.await();
  9. } finally {
  10. lock.unlock();
  11. }
  12. }
  13. private static void signal() {
  14. lock.lock();
  15. try {
  16. condition.signalAll();
  17. } finally {
  18. lock.unlock();
  19. }
  20. }
  21. public static void main(String[] args) throws InterruptedException {
  22. Thread thread1 = new Thread(() -> {
  23. for (int i = 0; i < 10; i++) {
  24. try {
  25. Thread.sleep(100);
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. count++;
  30. System.out.println(Thread.currentThread().getName() + " increase count to " + count);
  31. if (count == 5) {
  32. signal();
  33. }
  34. }
  35. });
  36. Thread thread2 = new Thread(() -> {
  37. try {
  38. await();
  39. System.out.println(Thread.currentThread().getName() + " receive signal, count is " + count);
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. });
  44. thread1.start();
  45. thread2.start();
  46. thread1.join();
  47. thread2.join();
  48. }
  49. }

在这个示例中,使用了 Lock 接口和 Condition 接口来定义了一个计数器,线程1每次增加计数器的值并判断是否达到条件,当计数器达到条件时调用 signal() 方法通知线程2,线程2等待条件成立后执行相应的操作。

信号量

  1. public class SemaphoreExample {
  2. private static Semaphore semaphore = new Semaphore(2);
  3. private static void doWork() throws InterruptedException {
  4. semaphore.acquire();
  5. System.out.println(Thread.currentThread().getName() + " start working");
  6. Thread.sleep(1000);
  7. System.out.println(Thread.currentThread().getName() + " finish working");
  8. semaphore.release();
  9. }
  10. public static void main(String[] args) throws InterruptedException {
  11. Thread thread1 = new Thread(() -> {
  12. try {
  13. doWork();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. });
  18. Thread thread2 = new Thread(() -> {
  19. try {
  20. doWork();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. });
  25. Thread thread3 = new Thread(() -> {
  26. try {
  27. doWork();
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. });
  32. thread1.start();
  33. thread2.start();
  34. thread3.start();
  35. thread1.join();
  36. thread2.join();
  37. thread3.join();
  38. }
  39. }

在这个示例中,使用了 Semaphore 类来定义了一个信号量,线程1、线程2、线程3都需要获取信号量才能进行工作,每次执行 doWork() 方法需要占用资源,执行完毕后释放信号量。

管道

  1. public class PipeExample {
  2. static class WriterThread extends Thread {
  3. private PipedOutputStream output;
  4. WriterThread(PipedOutputStream output) {
  5. this.output = output;
  6. }
  7. @Override
  8. public void run() {
  9. try {
  10. for(int i=1;i<=10;i++) {
  11. output.write(i);
  12. System.out.println("写入数据:" + i);
  13. Thread.sleep(1000);
  14. }
  15. } catch(Exception e) {
  16. e.printStackTrace();
  17. } finally {
  18. try {
  19. output.close();
  20. } catch(Exception e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. }
  26. static class ReaderThread extends Thread {
  27. private PipedInputStream input;
  28. ReaderThread(PipedInputStream input) {
  29. this.input = input;
  30. }
  31. @Override
  32. public void run() {
  33. try {
  34. int value;
  35. while((value=input.read()) != -1) {
  36. System.out.println("读取数据:" + value);
  37. }
  38. } catch(Exception e) {
  39. e.printStackTrace();
  40. } finally {
  41. try {
  42. input.close();
  43. } catch(Exception e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. }
  48. }
  49. public static void main(String[] args) throws IOException {
  50. PipedOutputStream output = new PipedOutputStream();
  51. PipedInputStream input = new PipedInputStream(output);
  52. Thread thread1 = new WriterThread(output);
  53. Thread thread2 = new ReaderThread(input);
  54. thread1.start();
  55. thread2.start();
  56. try {
  57. thread1.join();
  58. thread2.join();
  59. } catch (InterruptedException e) {
  60. e.printStackTrace();
  61. }
  62. }
  63. }

在这个示例中,使用了 PipedOutputStream 类和 PipedInputStream 类来定义了一个管道,线程1向管道中写入数据,线程2从管道中读取数据,通过管道来实现两个线程之间的通信。

发表评论

表情:
评论列表 (有 0 条评论,133人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java之间线通信

    Java之间线程的通信 篮子 生产者 消费者 测试类 篮子里面放水果,默认没有水果。生产者生产水果之后进入等待状态,并通知消费者购买水果。

    相关 JAVA线之间如何通信

    两种: 1.共享内存(共享内存模型:线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来 隐式 进行通信,例如可以使用volatile 修饰变量) 2.消息