`
bo_hai
  • 浏览: 565887 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

一个容易产生死锁的线程协作

 
阅读更多

程序要做的功能是:模似生产者与消费者。代码如下:

public class AddEggThread implements Runnable {

	private Plate plate;
	private Object egg = new Object();
	public AddEggThread(Plate plate) {
		this.plate = plate;
	}
	
	@Override
	public void run() {
		plate.addEgg(egg);
	}
	
}

 

public class GetEggThread implements Runnable {

	private Plate plate;
	public GetEggThread(Plate plate) {
		this.plate = plate;
	}
	@Override
	public void run() {
		plate.getEgg();
	}

}

 

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Plate {

	private List<Object> eggs = new ArrayList<Object>();
	
	public  synchronized Object getEgg() {
		while(eggs.size() == 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Object egg = eggs.get(0);
		eggs.clear(); //清空
		notify(); // 唤醒阻塞队列的某线程进入就绪队列
		System.out.println("拿到鸡蛋");
		return egg;
	}
	
	public synchronized void addEgg(Object egg) {
		while (eggs.size() > 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		eggs.add(egg);
		notify();
		System.out.println("放入鸡蛋");
	}

	public static void main(String[] args) {
		Plate plate = new Plate();
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			exec.execute(new GetEggThread(plate));
			exec.execute(new AddEggThread(plate));
		}
		exec.shutdown();
	}
}

 执行程序,偶尔会产生死锁。回家后,在书上找到了解决问题的办法。先上代码:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Plate {

	private List<Object> eggs = new ArrayList<Object>();
	
	public  synchronized Object getEgg() {
		while(eggs.size() == 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Object egg = eggs.get(0);
		eggs.clear(); //清空
		notifyAll(); // 唤醒阻塞队列的某线程进入就绪队列
		System.out.println("拿到鸡蛋");
		return egg;
	}
	
	public synchronized void addEgg(Object egg) {
		while (eggs.size() > 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		eggs.add(egg);
		notifyAll();
		System.out.println("放入鸡蛋");
	}

	public static void main(String[] args) {
		Plate plate = new Plate();
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			exec.execute(new GetEggThread(plate));
			exec.execute(new AddEggThread(plate));
		}
		exec.shutdown();
	}
}

 认真比较两段代码的差异,不难发现:notify() 改成 notifyAll()。为什么使用notify会导致死锁呢?可以解释为:

notify() 只会唤醒一个等待的线程,被唤醒的线程不一定能获当前释放的锁。

使用notify()而不是notifyAll()是一种优化。使用notify时,在众多等待的任务中只有一个会被唤醒。因此如果你希望使用notify(),就必须保证被唤醒的是恰当的任务。

在实际的程序中,很难做到使用notify()只唤醒恰当的任务(只有两个线程的情况除外)。因此,更多的时候我们使用notifyAll()。notifyAll() 将唤醒“所有正在等待的任务”这个说法并不正确。只唤醒等待当前锁的线程。

分享到:
评论

相关推荐

    Java多线程产生死锁的必要条件

    Java 多线程产生死锁的必要条件包括必须有两个或以上的线程、必须有两个临界资源、两个线程,每个线程都获取了其中的一个锁、不可剥夺。通过了解这些必要条件,我们可以更好地避免死锁,提高程序的稳定性和可靠性。

    java线程死锁实例

    Java线程死锁是多线程编程中一个常见的问题,它发生在两个或多个线程相互等待对方释放资源,导致它们都无法继续执行的情况。死锁的发生通常涉及到四个必要条件:互斥、请求与保持、不剥夺和循环等待。理解并解决Java...

    第20章 Part3 多线程互斥与协作.pdf

    - 假设我们有一个存款线程和一个取款线程,为了保证它们的协作,我们可以采用以下方案: 1. 使用锁来确保对`balance`变量的访问是互斥的。 2. 存款线程在完成存款后使用`notify()`方法通知取款线程。 3. 取款线程...

    操作系统-死锁

    死锁是发生在一组相互竞争或协作的进程与线程之间的一个非正常现象。 死锁是所有操作系统都面临着的潜在问题,操作系统除了需要预防死锁、避免死锁外,还需要能够检测死锁,并从死锁中进行恢复。

    线程编程 四个线程...

    Thread 类提供了一个基本的线程实现,而 Runnable 接口则提供了一个更加灵活的线程实现方式。 在给定的代码中,我们可以看到四个线程 t1、t2、t3 和 t4。每个线程都实现了 Runnable 接口,并 override 了 run 方法...

    Java多线程机制和线程之间的协作

    Java多线程机制是编程中一个重要的概念,它允许程序同时执行多个任务,提升程序的效率和响应性。在Java中,线程是程序执行的基本单元,比进程更细粒度,一个进程可以包含多个线程。每个线程有自己的生命周期,包括...

    关于多线程的一个小程序

    在MFC中,CWinThread类代表了一个线程,它提供了一系列方法来管理线程的生命周期,如AfxBeginThread用于创建新线程,以及消息队列的处理等。 在多线程编程中,有以下几个关键概念: 1. 线程安全:当多个线程访问...

    多线程之间的线程通信

    另一个问题是**死锁(Deadlock)**,即两个或更多线程相互等待对方释放资源,导致所有线程都无法继续执行。 为了避免这些问题,开发者需要遵循一些最佳实践,如最小化共享状态、使用线程安全的数据结构、正确地使用...

    进程与线程的一个简单解释

    然而,一个进程内部可以包含多个线程,这些线程共享同一份进程资源,如内存空间。 线程则是执行的最小单元,它是在进程中执行的一条指令序列。相比进程,线程创建和销毁的成本较低,因为它们共享同一进程的内存空间...

    C++封装的一个跨平台的线程类和锁类

    本项目提供了一个C++封装的线程类和锁类,使得开发者可以方便地在Linux和Windows平台上编写多线程代码。 线程类通常包括创建、启动、同步以及销毁线程等功能。在这个实现中,线程类可能包含以下关键点: 1. **构造...

    笔记-1、线程基础、线程之间的共享和协作2

    `join()`方法让一个线程等待另一个线程完成,而`sleep()`方法则让线程暂时休眠,两者都不会释放锁。 在高并发编程中,需要注意线程安全问题,如死锁、竞态条件和活锁。适当的线程池管理可以有效控制线程数量,避免...

    java关于线程的实验代码

    synchronized可以修饰方法或代码块,确保同一时间只有一个线程可以执行特定代码。另外,wait()和notify()、notifyAll()方法用于线程间的通信,实现线程的互斥和协作。 四、线程优先级 Java线程有10个优先级,从MIN_...

    produceconsumer_QWaitCondition_QSemaphore_死锁_

    `QWaitCondition` 是一个条件变量,它允许线程等待特定条件的发生。当条件满足时,其他线程可以唤醒等待的线程。`QSemaphore` 是一个信号量,用于控制对共享资源的访问,可以限制同时访问资源的线程数量。这两种工具...

    打开一个线程_打开一个线程_

    在多线程环境中,一个进程可以包含多个并发执行的线程,这些线程共享同一块内存空间,允许它们高效地协作完成任务。LabVIEW,全称Laboratory Virtual Instrument Engineering Workbench,是一款由美国国家仪器公司...

    java线程程序实例

    例如,线程A持有资源1并请求资源2,而线程B持有资源2并请求资源1,这样就形成了一个僵局。避免死锁的方法包括资源预分配、死锁预防、死锁避免和死锁检测与恢复策略。在Java中,我们可以使用try-with-resources语句,...

    多线程demo/java多线程练习

    - **CyclicBarrier**:允许一组线程等待彼此到达某个屏障点,然后一起继续执行,适用于多线程协作场景。 - **Semaphore**:信号量,用于控制同时访问特定资源的线程数量,通过acquire()获取一个许可,如果没有就...

    java 多线程示例

    这在多线程协作时非常有用。 8. **守护线程(Daemon Thread)** 守护线程不会阻止程序的退出,如JVM的垃圾收集器就是守护线程。可以通过`setDaemon(true)`将线程设置为守护线程。 9. **线程池** Java的`...

    多线程实例,线程实例

    1. 等待/通知(wait()和notify()):用于线程间的协作,一个线程等待另一个线程的通知后再继续执行。 2. 线程同步:使用synchronized关键字实现,防止多个线程同时访问同一块代码,避免数据不一致。 3. 守护线程...

    JAVA多线程编程技术PDF

    死锁是多线程中的一个常见问题,当两个或更多线程相互等待对方释放资源而无法继续执行时,就会发生死锁。为避免死锁,我们需要遵循一些原则,如避免循环等待,或者使用死锁检测和预防算法。 Java还提供了一些线程池...

    Java多线程练习题

    1. 线程优先级:每个线程都有一个优先级,优先级高的线程更容易被调度执行,但并不保证绝对优先。 2.守护线程(Daemon Thread):后台运行的线程,当所有非守护线程结束时,守护线程也会自动结束。 六、线程池 Java...

Global site tag (gtag.js) - Google Analytics