- 浏览: 34476 次
- 性别:
- 来自: 南京
最新评论
问题的描述
启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20....以此类推, 直到打印到75. 程序的输出结果应该为:
线程1: 1
线程1: 2
线程1: 3
线程1: 4
线程1: 5
线程2: 6
线程2: 7
线程2: 8
线程2: 9
线程2: 10
...
线程3: 71
线程3: 72
线程3: 73
线程3: 74
线程3: 75
解法一: 采用原始的synchronized, wait(), notify(), notifyAll()等方式控制线程.
解法二: 采用JDK1.5并发包提供的Lock, Condition等类的相关方法控制线程.
总结: 对比解法一和解法二, 显然解法二是更好的解决方案. 解法一的问题在于无法进行精确唤醒, 比如线程1执行完打印任务并调用pn.notifyAll()方法后, 3个线程将再次竞争锁, 而不是精确唤醒线程2. 虽然线程2最终将赢得锁, 下一次的打印任务也肯定会由线程2执行, 但是竞争的持续时间是不可预知的, 只能看线程2的人品.
最糟糕的情形可以是: 线程3竞争到了锁, 紧接着wait. 接下来线程1也竞争到了锁, 然后线程1也wait. 此时就再也没有其他线程跟线程2竞争了, 线程2终于艰难的赢得了锁...
留下3个问题供有兴趣的朋友思考:
1. 解法一和解法二中的while (state != xx)是否可以换成if(state != xx), 为什么?
2. 解法一的中的pn.notifyAll()是否可以换成pn.notify(), 为什么?
3. 是否可以用wait(), notify(), notifyAll()等方法完成类似解法二的精确唤醒, 请给出方案或代码.--这个问题我思考了很久, 却没有头绪. 关键的困难在于必须调用pn的wait()方法和notifyAll()方法, 而不能是其他对象的wait()和notifyAll()方法. 如果你有思路, 还望在博客中留言, 不甚感激!
启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20....以此类推, 直到打印到75. 程序的输出结果应该为:
线程1: 1
线程1: 2
线程1: 3
线程1: 4
线程1: 5
线程2: 6
线程2: 7
线程2: 8
线程2: 9
线程2: 10
...
线程3: 71
线程3: 72
线程3: 73
线程3: 74
线程3: 75
解法一: 采用原始的synchronized, wait(), notify(), notifyAll()等方式控制线程.
public class NumberPrintDemo { // n为即将打印的数字 private static int n = 1; // state=1表示将由线程1打印数字, state=2表示将由线程2打印数字, state=3表示将由线程3打印数字 private static int state = 1; public static void main(String[] args) { final NumberPrintDemo pn = new NumberPrintDemo(); new Thread(new Runnable() { public void run() { // 3个线程打印75个数字, 单个线程每次打印5个连续数字, 因此每个线程只需执行5次打印任务. 3*5*5=75 for (int i = 0; i < 5; i++) { // 3个线程都使用pn对象做锁, 以保证每个交替期间只有一个线程在打印 synchronized (pn) { // 如果state!=1, 说明此时尚未轮到线程1打印, 线程1将调用pn的wait()方法, 直到下次被唤醒 while (state != 1) try { pn.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 当state=1时, 轮到线程1打印5次数字 for (int j = 0; j < 5; j++) { // 打印一次后n自增 System.out.println(Thread.currentThread().getName() + ": " + n++); } System.out.println(); // 线程1打印完成后, 将state赋值为2, 表示接下来将轮到线程2打印 state = 2; // notifyAll()方法唤醒在pn上wait的线程2和线程3, 同时线程1将退出同步代码块, 释放pn锁. // 因此3个线程将再次竞争pn锁 // 假如线程1或线程3竞争到资源, 由于state不为1或3, 线程1或线程3将很快再次wait, 释放出刚到手的pn锁. // 只有线程2可以通过state判定, 所以线程2一定是执行下次打印任务的线程. // 对于线程2来说, 获得锁的道路也许是曲折的, 但前途一定是光明的. pn.notifyAll(); } } } }, "线程1").start(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 5; i++) { synchronized (pn) { while (state != 2) try { pn.wait(); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 5; j++) { System.out.println(Thread.currentThread().getName() + ": " + n++); } System.out.println(); state = 3; pn.notifyAll(); } } } }, "线程2").start(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 5; i++) { synchronized (pn) { while (state != 3) try { pn.wait(); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 5; j++) { System.out.println(Thread.currentThread().getName() + ": " + n++); } System.out.println(); state = 1; pn.notifyAll(); } } } }, "线程3").start(); } }4
解法二: 采用JDK1.5并发包提供的Lock, Condition等类的相关方法控制线程.
public class NumberPrint implements Runnable { private int state = 1; private int n = 1; // 使用lock做锁 private ReentrantLock lock = new ReentrantLock(); // 获得lock锁的3个分支条件 private Condition c1 = lock.newCondition(); private Condition c2 = lock.newCondition(); private Condition c3 = lock.newCondition(); @Override public void run() { new Thread(new Runnable() { public void run() { for (int i = 0; i < 5; i++) { try { // 线程1获得lock锁后, 其他线程将无法进入需要lock锁的代码块. // 在lock.lock()和lock.unlock()之间的代码相当于使用了synchronized(lock){} lock.lock(); while (state != 1) try { // 线程1竞争到了lock, 但是发现state不为1, 说明此时还未轮到线程1打印. // 因此线程1将在c1上wait // 与解法一不同的是, 三个线程并非在同一个对象上wait, 也不由同一个对象唤醒 c1.await(); } catch (InterruptedException e) { e.printStackTrace(); } // 如果线程1竞争到了lock, 也通过了state判定, 将执行打印任务 for (int j = 0; j < 5; j++) { System.out.println(Thread.currentThread().getName() + ": " + n++); } System.out.println(); // 打印完成后将state赋值为2, 表示下一次的打印任务将由线程2执行 state = 2; // 唤醒在c2分支上wait的线程2 c2.signal(); } finally { // 打印任务执行完成后需要确保锁被释放, 因此将释放锁的代码放在finally中 lock.unlock(); } } } }, "线程1").start(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 5; i++) { try { lock.lock(); while (state != 2) try { c2.await(); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 5; j++) { System.out.println(Thread.currentThread().getName() + ": " + n++); } System.out.println(); state = 3; c3.signal(); } finally { lock.unlock(); } } } }, "线程2").start(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 5; i++) { try { lock.lock(); while (state != 3) try { c3.await(); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 5; j++) { System.out.println(Thread.currentThread().getName() + ": " + n++); } System.out.println(); state = 1; c1.signal(); } finally { lock.unlock(); } } } }, "线程3").start(); } public static void main(String[] args) { new NumberPrint().run(); } }4
总结: 对比解法一和解法二, 显然解法二是更好的解决方案. 解法一的问题在于无法进行精确唤醒, 比如线程1执行完打印任务并调用pn.notifyAll()方法后, 3个线程将再次竞争锁, 而不是精确唤醒线程2. 虽然线程2最终将赢得锁, 下一次的打印任务也肯定会由线程2执行, 但是竞争的持续时间是不可预知的, 只能看线程2的人品.
最糟糕的情形可以是: 线程3竞争到了锁, 紧接着wait. 接下来线程1也竞争到了锁, 然后线程1也wait. 此时就再也没有其他线程跟线程2竞争了, 线程2终于艰难的赢得了锁...
留下3个问题供有兴趣的朋友思考:
1. 解法一和解法二中的while (state != xx)是否可以换成if(state != xx), 为什么?
2. 解法一的中的pn.notifyAll()是否可以换成pn.notify(), 为什么?
3. 是否可以用wait(), notify(), notifyAll()等方法完成类似解法二的精确唤醒, 请给出方案或代码.--这个问题我思考了很久, 却没有头绪. 关键的困难在于必须调用pn的wait()方法和notifyAll()方法, 而不能是其他对象的wait()和notifyAll()方法. 如果你有思路, 还望在博客中留言, 不甚感激!
发表评论
-
javaTrim方法扩展:Trim掉指定字符
2017-01-04 09:45 622public static String myTrim(Str ... -
Java中使用C3P0连接池
2017-01-03 16:09 3471、c3p0.properties c3p0.driv ... -
Java四种线程池的使用
2016-12-23 17:25 492Java通过Executors提供四种线程池,分别为: new ... -
JAVA在线编译器模拟
2016-12-23 16:54 506有很多网站提供在线编 ... -
jvm内存分析
2016-12-23 09:26 391http://www.cnblogs.com/ITtangta ... -
面向对象三大基本特性,五大基本原则
2016-12-18 21:40 308透切理解面向对象三大基本特性是理解面向对象五大基本原则的基础. ... -
接口和抽象类有什么区别
2016-12-18 21:38 404接口和抽象类有什么区别 你选择使用接口和抽象类的依据是什么? ... -
使用Java线程并发库实现两个线程交替打印的线程题
2016-12-12 11:22 497背景:是这样的今天在地铁上浏览了以下网页,看到网上一朋友问了一 ... -
推荐书目与知识点记录
2016-12-08 19:38 3881、Java序列化 2、线程通信:比如最简单的2个线程轮流 ... -
maven初次使用问题记录
2016-03-15 21:59 460【1】下载安装 下载的免安装版本,直接配置maven_ho ... -
eclipse远程调试Tomcat方法
2016-03-15 19:18 5401、Linux中配置tomcat在catalina.sh中添 ... -
Excel导入MySql数据库批量操作
2015-12-31 10:07 1650最近在帮朋友做一个计时提醒系统,大致业务如下: 某 ...
相关推荐
总结来说,Java实现多线程轮流打印数字需要考虑线程同步和数据一致性问题。使用`volatile`关键字和并发工具类能有效解决这些问题,同时,`jstack`工具是调试并发问题的有力助手。理解这些概念和技巧,对于编写高效、...
java多线程每个线程挨着打印ABC的4种实现方式,有4个线程t1、t2、t3、t4,t1打印A后t2打印A再t3打印A再t4打印A,然后从新回到t1打印B再t2打印B...t4打印B... 4个线程轮流打印abc... 一个线程可以理解为一个人,打印...
"多线程调试打印日志类"是一个专门设计用于在多线程环境中记录和打印日志的C++类。此类实现了一个单例模式,确保在整个应用程序中只有一个实例存在,从而避免了资源竞争和日志混乱的问题。 首先,单例模式是一种...
Qt案例之利用QThread类实现简单多线程案例循环打印数字,可参考文章:https://blog.csdn.net/didi_ya/article/details/122661092
在本例中,`AutoResetEvent`被用来控制线程打印数字的顺序,确保每个线程在正确的时间打印数字。 下面详细解释这个程序的工作原理: 1. **线程创建**:程序首先创建多个线程,每个线程负责打印一定范围内的数字。...
使用多线程和阻塞队列实现了ABC字母按顺序轮流打印
根据实验需求,我们需要创建两个子线程,分别打印从 1 到 99 和从 100 到 199 的数字,并且这两个线程需要交替打印数字。 **3.1 创建打印线程** 首先,我们创建两个类 `Test1` 和 `Test2` 来代表两个子线程,它们...
在本文中,我们将深入探讨如何使用C#编程语言在Windows操作系统环境下实现多线程同步打印文章的实验。这个实验的核心是创建两个独立的线程,一个用于文章的下载,另一个用于文章的打印,同时利用线程同步机制确保...
在处理耗时操作如打印时,多线程尤其有用,可以确保用户界面不会被阻塞,提供更好的用户体验。本文将深入探讨如何在C#中利用多线程来控制打印的暂停和继续。 首先,我们需要了解C#中的线程基础。`System.Threading`...
本主题聚焦于如何使用Java实现10个线程按照顺序打印数字1到100。这种问题通常通过线程间通信和同步机制来解决,如`synchronized`关键字、`wait()`、`notify()`或`notifyAll()`方法,以及`Semaphore`、`CyclicBarrier...
本文将深入探讨“文件打印问题”的多线程实现,这涉及到线程同步和互斥访问资源,以及如何在VC++6.0环境下运行和调试代码。 首先,让我们了解线程的基本概念。线程是程序执行的最小单位,每个线程都有自己的执行...
在`asnc-print-different-logfile`这个压缩包中,可能包含了一个示例项目,用于演示如何配置和使用Log4j2实现异步多线程打印。项目可能包含以下元素: 1. `pom.xml`:Maven项目的配置文件,定义了依赖项和构建指令...
这样,两个线程就可以交替打印数字,保证了顺序的正确性。 在`main()`函数中,我们创建了两个线程`t1`和`t2`,分别执行`printeven()`和`printodd()`,最后使用`join()`等待两个线程执行完毕,确保程序不会提前结束...
运行`Example02`类,我们将看到类似图10-2所示的结果,其中`蜘蛛侠`和`钢铁侠`线程以及主线程`main`交替打印数字。这是因为线程的执行顺序是不确定的,取决于操作系统的调度,这正是多线程并发执行的特点。 总结...
本文将详细讲解如何利用MFC实现一个支持多线程打印调试日志的控件,并探讨其中涉及的关键技术,如内存池、锁机制以及多线程编程。 1. **MFC打印日志** MFC中的COutputWnd或CDocument等类通常用于处理输出,但它们...
一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用...
Qt 多线程及简单实例 demo。 多线程的几大特点: 1.多线程的执行顺序无法保证,...本例中的线程(workthread类)实现的功能是,从0到9循环打印,0至9各占一排。 则该线程的具体实现详见demo。 demo环境为qt5.9 64位
在编程领域,多线程是实现并发执行任务的重要机制,特别是在现代计算机系统中,多核处理器使得多线程成为提高程序性能的关键手段。C#语言提供了丰富的多线程支持,让我们能够编写出高效的多线程应用程序。在这个"多...
多线程顺序打印
本项目标题为“vc++分别用单-多线程读取数字”,这意味着我们将探讨如何在Visual C++(简称VC++)环境下,通过单线程和多线程的方式实现数字的读取。以下是对这一主题的详细阐述。 1. **单线程编程**: 在单线程...