最近学习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锁并进入休眠。
两种实现其他一些不同的地方就不赘述了。
相关推荐
Java中的`Object.wait()`、`Object.notify()`以及`Object.notifyAll()`是多线程编程中的核心方法,它们用于实现线程间的协作与通信。这些方法是`Object`类的最终原生(`final native`)方法,这意味着它们的实现位于...
通过本文的介绍,我们了解到`wait()`和`notify()`机制是Java多线程中非常重要的同步工具。正确使用这些工具可以帮助我们更好地管理线程间的协作,避免死锁和资源竞争等问题。在实际应用中,需要注意对象锁的获取和...
在Java中,`wait()` 和 `notify()` 方法是实现线程间通信和协作的重要工具,它们属于 `java.lang.Object` 类,这意味着所有类都默认继承了这两个方法。本文将详细探讨如何使用 `wait()` 和 `notify()` 来控制子线程...
- 线程通信:线程间可以通过wait()、notify()和notifyAll()方法进行通信,这些方法位于Object类中。Java 5引入了BlockingQueue和 Phaser等高级工具,使得线程间的协作更为方便。 - 线程状态:Java中的线程有五种...
此外,线程间的通信可能使用到wait()、notify()和notifyAll()方法,这些都是Object类的方法,用于在特定条件下唤醒等待的线程。另外,BlockingQueue接口也是一个常用的线程间通信工具,它提供了线程安全的队列操作,...
### Java多线程学习总结 #### 一、Java多线程基本概念 1. **线程状态** ...通过以上内容的学习和理解,我们可以更好地掌握Java多线程的基础知识和技术细节,为开发高效稳定的并发程序奠定坚实的基础。
在标题和描述中,我们看到文档的标题是“Java多线程高级技术.pdf”,说明该文档是关于Java编程语言中多线程编程的高级技术的介绍。描述部分重复了标题信息,没有额外提供内容。而文档中的部分内容表明,本文将涉及到...
Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,从而提高程序的效率和并发性。在Java中,线程被用来实现并发,使得程序可以在同一时间处理多个任务,比如用户界面更新、网络请求和计算等。线程的...
Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,提高了系统的效率和响应性。在本文中,我们将深入探讨Java多线程的相关知识点,这些内容来源于"java多线程学习记录"的文档。 1. **线程的概念与...
4. **线程同步**:为避免多线程环境下的数据竞争,Java提供了多种同步机制,如`synchronized`关键字、`wait()`, `notify()`和`notifyAll()`方法、`Lock`接口(如`ReentrantLock`)以及`java.util.concurrent`包中的...
在Java编程语言中,多线程是至关重要的一个特性,尤其在现代高性能计算和分布式系统设计中。通过理解和掌握多线程技术,开发者可以充分利用计算机的多核处理器资源,提高程序的并发性和响应速度。本篇文章将深入探讨...
同样,在`ServiceThread`中,我们可能使用`Object.wait()`和`Object.notify()`或者`Condition`来实现线程间的同步,确保每次只有一个客户正在接受服务。 ```java public class CustomerThread extends Thread { ...
Java中多线程编程中,wait、notify、notifyAll三个方法是非常重要的,它们都是Object对象的方法,用于线程之间的通信。下面我们将详细介绍这三个方法的使用和作用。 一、wait()方法 wait()方法是使当前线程自动...
Java多线程编程是Java开发中的重要组成部分,它允许...通过学习和实践"Java多线程编程核心技术.zip"中的内容,开发者能深入理解Java多线程的原理和应用,提升软件并发处理能力,为构建高效、稳定的应用打下坚实基础。
2. wait()、notify()和notifyAll():这些方法在Object类中定义,用于线程间的通信和协作。它们必须在同步环境中使用,以避免死锁。 3. Lock接口和ReentrantLock类:Java 5引入了Lock接口,提供了比synchronized更细...
Java多线程是Java编程中的一个关键领域,尤其在面试中常常被重点考察。下面将对Java多线程的核心知识点进行详细的阐述。 1. **线程的创建方式**: - 继承Thread类:创建一个新的类,继承自Thread类,并重写其run()...
在Java多线程编程中,wait和notify是两个非常重要的方法,它们都是Object类的方法,用于线程之间的通信和同步。下面我们将详细解释wait和notify的用法。 wait方法 wait方法是Object类的一个方法,用于让当前线程...
4. wait(), notify()和notifyAll():Object类的方法,用于线程间通信,必须在同步控制块或方法中使用。 四、线程池 1. ExecutorService:Java并发包提供的线程池接口,用于管理和控制线程,避免大量线程创建销毁...
Java多线程与并发是Java编程中至关重要的一个领域,特别是在构建高性能、高并发的应用时。线程基础是理解并发编程的关键,它涉及到线程的状态转换、创建与使用方式、同步与协作机制等方面。 线程有五种基本状态: 1...