java线程间通信

在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作

Java中线程通信协作的最常见的两种方式:

  一.syncrhoized加锁的线程的Object类的wait()/notify()/notifyAll()

  二.ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()

本篇先说第一种方式。

wait()、notify()和notifyAll()是Object类中的方法:

      1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

  2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)

  3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程

  4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程

       wait与notify实现线程间的通信实例:

面试题:子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序:

package ThreadDemo;
public class ThreadTest {
	private static boolean bShouldMain = false;//这里相当于定义了控制该谁执行的一个信号灯
	public static void main(String[] args) {
		//因为用到了匿名内部类,内部类访问的局部变量需要用final修饰
		final Business business = new Business();
		new Thread(
				new Runnable(){
					public void run() {
						for(int i=1;i<=50;i++){
							//这里使用类的字节码对象作为锁进行同步,但是当需要对同步进行分组时就不科学了
							business.sub(i);
						}
					}
				}
		).start();
 
		//main方法本身是一个线程,这里是主线程的运行代码
		for(int i=1;i<=50;i++){
			business.main(i);
		}
	}
}
 
class Business {
	private boolean bShouldSub = true;//最开始该子线程走
	public synchronized void sub(int i){
		while(!bShouldSub){//用while而不是if线程醒来还会再次进行判断,防止代码被伪唤醒,代码更健壮。还可以防止生产者消费者问题
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for(int j=1;j<=10;j++){
			System.out.println("sub thread sequence of "+j+", loop of "+i);
		}
		bShouldSub = false;
		this.notify();
	}
	public synchronized void main(int i){
		while(bShouldSub){
			try {
				this.wait();//这里的锁是this
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for(int j=1;j<=100;j++){
			System.out.println("main thread sequence of "+j+", loop of "+i);
		}
		bShouldSub = true;
		this.notify();
	}
}

经验:

  1. 要用到共同数据(包括同步锁)或共同算法的若干方法应该归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性
  2. 锁是上在要操作的资源的类的内部方法中,而不是线程代码中!好处是以后该类交给任何线程自然就同步了,而不需要考虑互斥同步的问题。
  3. Eclipse中将运行结果保存至文件的操作:Run as-->Run Configuration-->Common-->File处打钩然后选择一个文件

 

### Java 线程间通信的方法和机制 Java 中的线程间通信是指多个线程通过某种方式共享数据或控制流,从而实现协同工作的过程。以下是几种常见的线程间通信方法及其工作机制。 #### 1. 使用 `wait()` 和 `notify()/notifyAll()` `Object` 类提供了三个用于线程间通信的核心方法:`wait()`、`notify()` 和 `notifyAll()`。这些方法必须在同步上下文中调用(即在 synchronized 块或方法中)。 - **`wait()`**: 当前线程进入等待状态,直到其他线程调用了同一对象上的 `notify()` 或 `notifyAll()` 方法。 - **`notify()`**: 随机唤醒一个正在此对象上等待的单个线程。 - **`notifyAll()`**: 唤醒在此对象上等待的所有线程。 这种方法适用于生产者消费者模式等场景[^1]。 ```java class SharedResource { private int data; private boolean available = false; public synchronized void produce(int value) throws InterruptedException { while (available) { wait(); // 如果资源不可用,则当前线程等待 } data = value; available = true; notifyAll(); // 通知消费线程可以继续运行 } public synchronized int consume() throws InterruptedException { while (!available) { wait(); // 如果资源不可用,则当前线程等待 } available = false; notifyAll(); // 通知生产线程可以继续运行 return data; } } ``` --- #### 2. 使用 `Lock` 和条件变量 除了传统的 `synchronized` 关键字外,还可以使用更高层次的锁机制——`ReentrantLock` 及其关联的条件队列 (`Condition`) 来实现线程间通信。这种方式更加灵活,允许指定不同的条件进行信号传递[^3]。 ```java import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class SharedResourceWithLock { private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private int data; private boolean available = false; public void produce(int value) throws InterruptedException { lock.lock(); try { while (available) { condition.await(); // 生产线程等待 } data = value; available = true; condition.signalAll(); // 唤醒消费线程 } finally { lock.unlock(); } } public int consume() throws InterruptedException { lock.lock(); try { while (!available) { condition.await(); // 消费线程等待 } available = false; condition.signalAll(); // 唤醒生产线程 return data; } finally { lock.unlock(); } } } ``` --- #### 3. 利用阻塞队列 Java 的 `java.util.concurrent` 包提供了一系列高效的线程安全集合类,其中最常用的便是阻塞队列(Blocking Queue),如 `LinkedBlockingQueue` 和 `ArrayBlockingQueue`。这类容器本身实现了生产和消费逻辑中的线程协调功能[^2]。 ```java import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class ProducerConsumerExample { static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10); public static class Producer implements Runnable { @Override public void run() { try { for (int i = 0; i < 20; i++) { System.out.println("Producing " + i); queue.put(i); // 自动处理满队列的情况 Thread.sleep(100); } } catch (InterruptedException e) {} } } public static class Consumer implements Runnable { @Override public void run() { try { while (true) { Integer item = queue.take(); // 自动处理空队列的情况 System.out.println("Consumed " + item); if (item == 19) break; // 终止条件 } } catch (InterruptedException e) {} } } public static void main(String[] args) throws Exception { Thread producerThread = new Thread(new Producer()); Thread consumerThread = new Thread(new Consumer()); producerThread.start(); consumerThread.start(); producerThread.join(); consumerThread.join(); } } ``` --- #### 4. 使用原子变量 对于简单的计数器操作或其他轻量级需求,可以直接利用 `AtomicInteger`、`AtomicLong` 等原子类完成无锁化的线程间通信。 ```java import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger counter = new AtomicInteger(0); public int incrementAndGet() { return counter.incrementAndGet(); // 完成自增并返回新值 } public int get() { return counter.get(); // 获取当前值 } } // 测试代码省略... ``` --- #### 5. 使用 `CountDownLatch`, `CyclicBarrier` 和 `Semaphore` 这些工具类分别适合不同类型的线程协作场景: - **`CountDownLatch`**: 让某个线程等待若干事件完成后才继续执行。 - **`CyclicBarrier`**: 多个线程到达屏障后再一起向前推进。 - **`Semaphore`**: 控制同时访问某资源的最大线程数量。 示例代码如下: ```java import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(3); for (int i = 0; i < 3; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " is done."); latch.countDown(); // 减少倒计数值 }).start(); } latch.await(); // 主线程会一直等到计数归零 System.out.println("All threads have finished their work."); } } ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值