`
NeverFlyAway
  • 浏览: 69507 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类

java多线程协作object.notify和object.wait

    博客分类:
  • JDK
阅读更多

最近学习java多线程协作,发现网上很多文章写得不清晰,甚至是错误的。所以自己研究了一下多线程协作的写法,通过例子进行说明:三个线程,A线程输出6遍A,B线程输出6遍B,C线程输出6遍C,要求按照ABC的顺序轮流唤醒进行输出。做了两种实现。

 

第一种实现:

 

public class Printer implements Runnable {

	private String name;
	private Integer times;
	private Printer next;

	public Printer(String name, Integer times) {
		super();
		this.name = name;
		this.times = times;
	}

	public void setNext(Printer next) {
		this.next = next;
	}

	@Override
	public void run() {
		while (times > 0) {
			synchronized (next) {
				synchronized (this) {
					try {
						Thread.sleep(1000);
					} catch (Exception e) {
						e.printStackTrace();
					}
					System.out.println(name + times);
					times = times - 1;
					this.notify();// 释放this的同步锁,synchronized代码块执行完释放
				}
				try {
					next.wait();// 释放next的同步锁,休眠当前Thread
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}

 

Junit测试类

 

public class ThreadTest {

	@Test
	public void test() throws InterruptedException {
		Printer printA = new Printer("A", 6);
		Printer printB = new Printer("B", 6);
		Printer printC = new Printer("C", 6);
		printA.setNext(printB);
		printB.setNext(printC);
		printC.setNext(printA);
		Thread threadA = new Thread(printA);
		Thread threadB = new Thread(printB);
		Thread threadC = new Thread(printC);
		threadA.start();
		Thread.sleep(10);//确保A线程先运行
		threadB.start();
		threadC.start();
		threadA.join();
		threadB.join();
		synchronized (printA) {
			printA.notify();//唤醒休眠的C线程,使C线程顺利结束
		}
		threadC.join();
	}

}

 

执行结果:

 

A6
B6
C6
A5
B5
C5
A4
B4
C4
A3
B3
C3
A2
B2
C2
A1
B1
C1

 

加上一些打印日志,方便查看程序的逻辑:

 

public class Printer implements Runnable {

	private String name;
	private Integer times;
	private Printer next;

	public Printer(String name, Integer times) {
		super();
		this.name = name;
		this.times = times;
	}

	public void setNext(Printer next) {
		this.next = next;
	}

	@Override
	public void run() {
		while (times > 0) {
			System.out.println(this.name +"想要获取"+ next.name +"锁"+ this.name +"锁");
			synchronized (next) {
				System.out.println(this.name + "成功获取" + next.name + "锁");
				synchronized (this) {
					System.out.println(this.name + "成功获取" + this.name + "锁");
					try {
						Thread.sleep(1000);
					} catch (Exception e) {
						e.printStackTrace();
					}
					System.out.println(name + times);
					times = times - 1;
					System.out.println(this.name + "释放" + this.name + "锁");
					this.notify();//释放this的同步锁,synchronized代码块执行完释放
				}
				try {
					System.out.println(this.name + "释放" + next.name + "锁");
					next.wait();//释放next的同步锁,休眠当前Thread
					System.out.println(this.name + "苏醒");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println(this.name + "结束");
	}
}

 

执行结果:

 

A想要获取B锁A锁
A成功获取B锁
A成功获取A锁
B想要获取C锁B锁
B成功获取C锁
C想要获取A锁C锁
A6
A释放A锁
A释放B锁
C成功获取A锁
B成功获取B锁
B6
B释放B锁
B释放C锁
A苏醒
C成功获取C锁
A想要获取B锁A锁
A成功获取B锁
C6
C释放C锁
C释放A锁
B苏醒
B想要获取C锁B锁
B成功获取C锁
A成功获取A锁
A5
A释放A锁
A释放B锁
C苏醒
B成功获取B锁
C想要获取A锁C锁
C成功获取A锁
B5
B释放B锁
B释放C锁
A苏醒
A想要获取B锁A锁
A成功获取B锁
C成功获取C锁
C5
C释放C锁
C释放A锁
B苏醒
A成功获取A锁
B想要获取C锁B锁
B成功获取C锁
A4
A释放A锁
A释放B锁
C苏醒
B成功获取B锁
C想要获取A锁C锁
C成功获取A锁
B4
B释放B锁
A苏醒
A想要获取B锁A锁
A成功获取B锁
B释放C锁
C成功获取C锁
C4
C释放C锁
C释放A锁
B苏醒
B想要获取C锁B锁
B成功获取C锁
A成功获取A锁
A3
A释放A锁
A释放B锁
C苏醒
B成功获取B锁
C想要获取A锁C锁
C成功获取A锁
B3
B释放B锁
B释放C锁
A苏醒
A想要获取B锁A锁
A成功获取B锁
C成功获取C锁
C3
C释放C锁
C释放A锁
B苏醒
B想要获取C锁B锁
B成功获取C锁
A成功获取A锁
A2
A释放A锁
A释放B锁
C苏醒
B成功获取B锁
C想要获取A锁C锁
C成功获取A锁
B2
B释放B锁
B释放C锁
A苏醒
A想要获取B锁A锁
A成功获取B锁
C成功获取C锁
C2
C释放C锁
C释放A锁
B苏醒
A成功获取A锁
B想要获取C锁B锁
B成功获取C锁
A1
A释放A锁
A释放B锁
C苏醒
B成功获取B锁
C想要获取A锁C锁
C成功获取A锁
B1
B释放B锁
A苏醒
A结束
B释放C锁
C成功获取C锁
C1
C释放C锁
C释放A锁
B苏醒
B结束
C苏醒
C结束

 

 

分析一下第一种实现:

notify和wait的基础知识就不赘述了,就重申一下重点:

 

  • 这两个方法必须写在synchronized的代码块中,因为必须获取object的对象锁之后才能执行该object的notify和wait方法。
  • notify方法无论写在synchronized的代码块中的任何一行都可以,因为notify方法在所属的synchronized代码块执行完成后才会执行。notify方法执行后会解锁该object,同时唤醒另一个正在等待该object的线程继续执行,同时本线程也会继续往下执行。
  • wait方法和notify方法不同之处在于,wait方法执行时,也会解锁该object,但是,是立刻解锁该object,而不是等到wait方法所属的synchronized代码块执行完成后才执行,wait方法执行后,在解锁该object的同时,object本身所属的线程进入休眠(或者说进入等待),然后需要该object再次执行notify方法或者notifyAll方法后,该线程才会被唤醒,代码才会继续往下执行。

 

第一种实现的设计思路就是:

 

假设A线程最先执行,A线程先获取B的对象锁,然后获取A的对象锁,两个锁同时获取之后,打印A,然后A线程解锁A的对象锁,唤醒正在等待A的对象锁的另一个线程(唤醒了线程C),然后A线程解锁B的对象锁,同时A线程进入休眠(或者说进入等待)。此时B线程早就已经获取了没人要的C对象锁,然后想要获取B对象锁,就能打印B了。B成功获取了A线程释放的B对象锁,打印B,打印之后B线程解锁B,同时唤醒了进入休眠的A线程,然后B线程解锁C,B线程进入休眠。C线程之前在被A线程唤醒的时候已经获取了A的对象锁,C线程此时又获取了B线程所释放的C对象锁,然后打印C,然后释放C锁,唤醒B线程,然后再释放了A锁,C线程进入休眠。A线程之前被B线程唤醒,已经获取了B锁,现在又获取了到了A锁,又可以打印第二遍A了,一轮ABC打印已经完成。

 

仔细查看运行结果就可以理解第一种实现的设计思路了。

synchronized (printA) {
	printA.notify();//唤醒休眠的C线程,使C线程顺利结束
}

可以将测试类中的这三行代码注释掉执行看看,方便你理解,为啥需要这么做。

 

吐槽:好像在说书一样,这是在导演家庭伦理剧吗

 

 

 

第二种实现:

 

public class Printer2 implements Runnable {

	private String name;
	private Integer times;
	private Thread current;
	private Thread next;

	public Printer2(String name, Integer times) {
		super();
		this.name = name;
		this.times = times;
	}

	public void setThread(Thread current, Thread next) {
		this.current = current;
		this.next = next;
	}

	@Override
	public void run() {
		while (times > 0) {
			synchronized (current) {
				synchronized (next) {
					System.out.println("start" + name);
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(name + times);
					times = times - 1;
					if (next.isAlive()) {
						next.notify();
					} else {
						next.start();
					}
				}
				try {
					current.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
}

 

Junit测试类:

 

public class ThreadTest {

	@Test
	public void test() throws InterruptedException {
		Printer2 printA = new Printer2("A", 6);
		Printer2 printB = new Printer2("B", 6);
		Printer2 printC = new Printer2("C", 6);
		Thread threadA = new Thread(printA);
		Thread threadB = new Thread(printB);
		Thread threadC = new Thread(printC);
		printA.setThread(threadA, threadB);
		printB.setThread(threadB, threadC);
		printC.setThread(threadC, threadA);
		threadA.start();
		Thread.sleep(100000);//让子弹飞一会
	}

}

 

执行结果:

 

startA
A6
startB
B6
startC
C6
startA
A5
startB
B5
startC
C5
startA
A4
startB
B4
startC
C4
startA
A3
startB
B3
startC
C3
startA
A2
startB
B2
startC
C2
startA
A1
startB
B1
startC
C1

 

第二种实现的思路:

 

通过Thread的notify和wait方法,同样是获取ThreadA的锁和ThreadB的锁,同时获取两个锁之后打印A,然后解锁ThreadB,唤醒ThreadB,解锁ThreadA,休眠ThreadA。

 

和实现一的不同之处在于,第一种先获取获取下一个打印object的锁,再获取自身object锁,然后打印,解锁自身,解锁下一个,同时本线程休眠。第二种先获取自身的Thread锁,然后获取下一个打印的Thread锁,然后打印,然后解锁并唤醒下一个Thread,然后解锁自身Thread锁并进入休眠。

 

两种实现其他一些不同的地方就不赘述了。

 

 

分享到:
评论

相关推荐

    Object.wait()与Object.notify()的用法详细解析

    Java中的`Object.wait()`、`Object.notify()`以及`Object.notifyAll()`是多线程编程中的核心方法,它们用于实现线程间的协作与通信。这些方法是`Object`类的最终原生(`final native`)方法,这意味着它们的实现位于...

    java多线程设计wait.docx

    通过本文的介绍,我们了解到`wait()`和`notify()`机制是Java多线程中非常重要的同步工具。正确使用这些工具可以帮助我们更好地管理线程间的协作,避免死锁和资源竞争等问题。在实际应用中,需要注意对象锁的获取和...

    Java多线程wait和notify

    在Java中,`wait()` 和 `notify()` 方法是实现线程间通信和协作的重要工具,它们属于 `java.lang.Object` 类,这意味着所有类都默认继承了这两个方法。本文将详细探讨如何使用 `wait()` 和 `notify()` 来控制子线程...

    smoker_java多线程_源码.zip

    - 线程通信:线程间可以通过wait()、notify()和notifyAll()方法进行通信,这些方法位于Object类中。Java 5引入了BlockingQueue和 Phaser等高级工具,使得线程间的协作更为方便。 - 线程状态:Java中的线程有五种...

    超简单Java多线程填表源码.zip

    此外,线程间的通信可能使用到wait()、notify()和notifyAll()方法,这些都是Object类的方法,用于在特定条件下唤醒等待的线程。另外,BlockingQueue接口也是一个常用的线程间通信工具,它提供了线程安全的队列操作,...

    java多线程学习总结.docx

    ### Java多线程学习总结 #### 一、Java多线程基本概念 1. **线程状态** ...通过以上内容的学习和理解,我们可以更好地掌握Java多线程的基础知识和技术细节,为开发高效稳定的并发程序奠定坚实的基础。

    Java多线程高级技术.pdf

    在标题和描述中,我们看到文档的标题是“Java多线程高级技术.pdf”,说明该文档是关于Java编程语言中多线程编程的高级技术的介绍。描述部分重复了标题信息,没有额外提供内容。而文档中的部分内容表明,本文将涉及到...

    Java多线程学习总结.pdf

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,从而提高程序的效率和并发性。在Java中,线程被用来实现并发,使得程序可以在同一时间处理多个任务,比如用户界面更新、网络请求和计算等。线程的...

    java多线程学习记录.rar

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,提高了系统的效率和响应性。在本文中,我们将深入探讨Java多线程的相关知识点,这些内容来源于"java多线程学习记录"的文档。 1. **线程的概念与...

    Java多线程的经典资料.rar

    4. **线程同步**:为避免多线程环境下的数据竞争,Java提供了多种同步机制,如`synchronized`关键字、`wait()`, `notify()`和`notifyAll()`方法、`Lock`接口(如`ReentrantLock`)以及`java.util.concurrent`包中的...

    Java多线程编程学习.pdf

    在Java编程语言中,多线程是至关重要的一个特性,尤其在现代高性能计算和分布式系统设计中。通过理解和掌握多线程技术,开发者可以充分利用计算机的多核处理器资源,提高程序的并发性和响应速度。本篇文章将深入探讨...

    java多线程模拟队列实现排队叫号

    同样,在`ServiceThread`中,我们可能使用`Object.wait()`和`Object.notify()`或者`Condition`来实现线程间的同步,确保每次只有一个客户正在接受服务。 ```java public class CustomerThread extends Thread { ...

    Java多线程中wait、notify、notifyAll使用详解

    Java中多线程编程中,wait、notify、notifyAll三个方法是非常重要的,它们都是Object对象的方法,用于线程之间的通信。下面我们将详细介绍这三个方法的使用和作用。 一、wait()方法 wait()方法是使当前线程自动...

    Java多线程编程核心技术.zip

    Java多线程编程是Java开发中的重要组成部分,它允许...通过学习和实践"Java多线程编程核心技术.zip"中的内容,开发者能深入理解Java多线程的原理和应用,提升软件并发处理能力,为构建高效、稳定的应用打下坚实基础。

    java多线程示例

    2. wait()、notify()和notifyAll():这些方法在Object类中定义,用于线程间的通信和协作。它们必须在同步环境中使用,以避免死锁。 3. Lock接口和ReentrantLock类:Java 5引入了Lock接口,提供了比synchronized更细...

    【Java核心知识面试】-java多线程50题.zip

    Java多线程是Java编程中的一个关键领域,尤其在面试中常常被重点考察。下面将对Java多线程的核心知识点进行详细的阐述。 1. **线程的创建方式**: - 继承Thread类:创建一个新的类,继承自Thread类,并重写其run()...

    java之wait,notify的用法([ 详解+实例 ])

    在Java多线程编程中,wait和notify是两个非常重要的方法,它们都是Object类的方法,用于线程之间的通信和同步。下面我们将详细解释wait和notify的用法。 wait方法 wait方法是Object类的一个方法,用于让当前线程...

    java,多线程实验.zip

    4. wait(), notify()和notifyAll():Object类的方法,用于线程间通信,必须在同步控制块或方法中使用。 四、线程池 1. ExecutorService:Java并发包提供的线程池接口,用于管理和控制线程,避免大量线程创建销毁...

    Java 多线程与并发(2-26)Java 并发 - 线程基础.pdf

    Java多线程与并发是Java编程中至关重要的一个领域,特别是在构建高性能、高并发的应用时。线程基础是理解并发编程的关键,它涉及到线程的状态转换、创建与使用方式、同步与协作机制等方面。 线程有五种基本状态: 1...

Global site tag (gtag.js) - Google Analytics