要求

启动N个线程, 这N个线程要不间断按顺序打印数字1-N. 将问题简化为3个线程无限循环打印1到3

方法一: 使用synchronized

三个线程无序竞争同步锁, 如果遇上的是自己的数字, 就打印. 这种方式会浪费大量的循环

  1. public class TestSequential1 {
  2. private volatile int pos = 1;
  3. private volatile int count = 0;
  4.  
  5. public void one(int i) {
  6. synchronized (this) {
  7. if (pos == i) {
  8. System.out.println("T-" + i + " " + count);
  9. pos = i % 3 + 1;
  10. count = 0;
  11. } else {
  12. count++;
  13. }
  14. }
  15. }
  16.  
  17. public static void main(String[] args) {
  18. TestSequential1 demo = new TestSequential1();
  19. for (int i = 1; i <=3; i++) {
  20. int j = i;
  21. new Thread(()->{
  22. while(true) {
  23. demo.one(j);
  24. }
  25. }).start();
  26. }
  27. }
  28. }

输出

  1. T-1 0
  2. T-2 5793
  3. T-3 5285
  4. T-1 2616
  5. T-2 33
  6. T-3 28
  7. T-1 22
  8. T-2 44
  9. T-3 6
  10. T-1 881
  11. T-2 118358
  12. T-3 247380
  13. T-1 30803
  14. T-2 29627
  15. T-3 52044
  16. ...

方法二: 使用synchronized配合wait()和notifyAll()

竞争同步锁时使用wait()和notifyAll(), 可以避免浪费循环

  1. public class TestSequential4 {
  2. private volatile int pos = 1;
  3. private volatile int count = 0;
  4. private final Object obj = new Object();
  5.  
  6. public void one(int i) {
  7. System.out.println(i + " try");
  8. synchronized (obj) {
  9. System.out.println(i + " in");
  10. try {
  11. while (pos != i) {
  12. count++;
  13. System.out.println(i + " wait");
  14. obj.wait();
  15. }
  16. System.out.println("T-" + i + " " + count);
  17. pos = i % 3 + 1;
  18. count = 0;
  19. obj.notifyAll();
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25.  
  26. public static void main(String[] args) {
  27. TestSequential4 demo = new TestSequential4();
  28. for (int i = 3; i >=1; i--) {
  29. int j = i;
  30. new Thread(()->{
  31. while(true) {
  32. demo.one(j);
  33. }
  34. }).start();
  35. }
  36. }
  37. }

输出

  1. 3 try
  2. 3 in
  3. 3 wait
  4. 2 try
  5. 2 in
  6. 2 wait
  7. 1 try
  8. 1 in
  9. T-1 2
  10. 1 try
  11. 1 in
  12. 1 wait
  13. T-2 1
  14. 2 try
  15. 2 in
  16. 2 wait
  17. T-3 1
  18. 3 try
  19. 3 in
  20. 3 wait
  21. 2 wait
  22. T-1 2
  23. 1 try
  24. 1 in
  25. 1 wait
  26. T-2 1
  27. 2 try
  28. 2 in
  29. 2 wait
  30. T-3 1
  31. 3 try
  32. 3 in
  33. 3 wait
  34. 2 wait
  35. T-1 2
  36. 1 try
  37. 1 in
  38. 1 wait
  39. T-2 1
  40. 2 try
  41. 2 in
  42. 2 wait
  43. T-3 1
  44. 3 try
  45. 3 in
  46. 3 wait
  47. 2 wait
  48. T-1 2
  49. 1 try
  50. 1 in
  51. 1 wait
  52. T-2 1
  53. 2 try
  54. 2 in
  55. 2 wait
  56. T-3 1
  57. 3 try
  58. 3 in
  59. 3 wait
  60. 2 wait
  61. T-1 2
  62. 1 try
  63. 1 in
  64. 1 wait
  65. T-2 1
  66. 2 try
  67. 2 in
  68. 2 wait
  69. T-3 1
  70. 3 try
  71. 3 in
  72. 3 wait
  73. 2 wait
  74. T-1 2
  75. 1 try
  76. 1 in
  77. 1 wait
  78. T-2 1
  79. 2 try
  80. 2 in
  81. 2 wait
  82. T-3 1
  83. 3 try
  84. 3 in
  85. 3 wait
  86. 2 wait
  87. T-1 2
  88. 1 try
  89. 1 in
  90. 1 wait
  91. T-2 1
  92. 2 try
  93. 2 in
  94. 2 wait
  95. T-3 1
  96. 3 try
  97. 3 in
  98. 3 wait
  99. 2 wait
  100. T-1 2
  101. 1 try
  102. 1 in
  103. 1 wait
  104. T-2 1
  105. 2 try
  106. 2 in
  107. 2 wait
  108. T-3 1
  109. 3 try
  110. 3 in
  111. 3 wait
  112. 2 wait
  113. T-1 2
  114. 1 try
  115. 1 in
  116. 1 wait
  117. T-2 1
  118. 2 try
  119. 2 in
  120. 2 wait
  121. T-3 1
  122. 3 try
  123. 3 in
  124. 3 wait
  125. 2 wait
  126. T-1 2
  127. ...

.

方法三: 使用可重入锁

用Lock做, 非公平锁, 三个线程竞争, 如果遇上的是自己的数字, 就打印. 这种方式也会浪费大量的循环

  1. public class TestSequential2 {
  2. private final Lock lock = new ReentrantLock();
  3. private volatile int pos = 1;
  4. private volatile int count = 0;
  5.  
  6. public void one(int i) {
  7. lock.lock();
  8. if (pos == i) {
  9. System.out.println("T-" + i + " " + count);
  10. pos = i % 3 + 1;
  11. count = 0;
  12. } else {
  13. count++;
  14. }
  15. lock.unlock();
  16. }
  17.  
  18. public static void main(String[] args) {
  19. TestSequential2 demo = new TestSequential2();
  20. for (int i = 1; i <=3; i++) {
  21. int j = i;
  22. new Thread(()->{
  23. while(true) {
  24. demo.one(j);
  25. }
  26. }).start();
  27. }
  28. }
  29. }

输出

  1. T-1 0
  2. T-2 0
  3. T-3 323
  4. T-1 54
  5. T-2 68964
  6. T-3 97642
  7. T-1 6504
  8. T-2 100603
  9. T-3 6989
  10. T-1 1313
  11. T-2 0
  12. T-3 183741
  13. T-1 233
  14. T-2 5081
  15. T-3 164367
  16. ..

.

方法四: 使用可重入锁, 启用公平锁

和3一样, 但是使用公平锁, 这种情况下基本上可以做到顺序执行, 偶尔会产生多一次循环

  1. private final Lock lock = new ReentrantLock(true);

输出

  1. T-1 0
  2. T-2 0
  3. T-3 0
  4. T-1 0
  5. T-2 0
  6. T-3 0
  7. T-1 0
  8. T-2 0
  9. T-3 0
  10. T-1 0
  11. T-2 0
  12. T-3 1
  13. T-1 1
  14. T-2 1
  15. T-3 1
  16. ...

.

方法五: 使用Condition

每个线程如果看到不是自己的计数, 就await(), 如果是自己的计数, 就完成打印动作, 再signalAll()所有其他线程去继续运行, 自己在下一个循环后, 即使又继续执行, 也会因为计数已经变了而await.

如果ReentrantLock构造参数使用true, 可以基本消除 ~await 这一步的输出.

  1. public class ReentrantLockCondition2 {
  2. private static Lock lock = new ReentrantLock();
  3. private static Condition condition = lock.newCondition();
  4. private volatile int state = 1;
  5.  
  6. private void handle(int state) {
  7. lock.lock();
  8. try {
  9. while(true) {
  10. while(this.state != state) {
  11. System.out.println(state + " ~await");
  12. condition.await();
  13. }
  14. System.out.println(state);
  15. this.state = state % 3 + 1;
  16. condition.signalAll();
  17. System.out.println(state + " await");
  18. condition.await();
  19. }
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. } finally {
  23. lock.unlock();
  24. }
  25. }
  26.  
  27. public static void main(String[] args) {
  28. ReentrantLockCondition2 rlc = new ReentrantLockCondition2();
  29. new Thread(()->rlc.handle(1)).start();
  30. new Thread(()->rlc.handle(2)).start();
  31. new Thread(()->rlc.handle(3)).start();
  32. }
  33. }

.

方法六: 使用多个Condition

给每个线程不同的condition. 这个和4的区别是, 可以用condition.signal()精确地通知对应的线程继续执行(在对应的condition上await的线程, 可能是多个). 这种情况下是可以多个线程都不unlock锁的情况下进行协作的. 注意下面的while(true)循环是在lock.lock()方法内部的.

  1. public class ReentrantLockCondition {
  2. private static Lock lock = new ReentrantLock();
  3. private static Condition[] conditions = {lock.newCondition(), lock.newCondition(), lock.newCondition()};
  4. private volatile int state = 1;
  5.  
  6. private void handle(int state) {
  7. lock.lock();
  8. try {
  9. while(true) {
  10. while(this.state != state) {
  11. conditions[state - 1].await();
  12. }
  13. System.out.println(state);
  14. this.state = state % 3 + 1;
  15. conditions[this.state - 1].signal();
  16. conditions[state - 1].await();
  17. }
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  24.  
  25. public static void main(String[] args) {
  26. ReentrantLockCondition rlc = new ReentrantLockCondition();
  27. new Thread(()->rlc.handle(1)).start();
  28. new Thread(()->rlc.handle(2)).start();
  29. new Thread(()->rlc.handle(3)).start();
  30. }
  31. }

.

Java多个线程顺序打印数字的更多相关文章

  1. Java n个线程轮流打印数字的问题

    一. 实现两个线程.轮流打印出数字.例如以下: bThread --> 10 aThread --> 9 bThread --> 8 aThread --> 7 bThread ...

  2. java启动3个线程轮流打印数字

    转自:http://blog.csdn.net/u014011112/article/details/50988769 http://blog.csdn.net/perrywork/article/d ...

  3. 【多线程基础】- 多个线程顺序打印ABC

    题目:3个线程名字分别是A,B,C 现在在console上连续打印10次 ABC . public class Test { public static void main(String[] args ...

  4. Linux 多线程按照线程顺序打印字符

    #include <stdio.h> #include <pthread.h> #include <unistd.h> ; pthread_mutex_t mute ...

  5. java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现

    注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...

  6. 使用Java实现三个线程交替打印0-74

    使用Java实现三个线程交替打印0-74 题目分析 三个线程交替打印,即3个线程是按顺序执行的.一个线程执行完之后,唤醒下一个线程,然后阻塞,等待被该线程的上一个线程唤醒.执行的顺序是一个环装的队列 ...

  7. 使用Java线程并发库实现两个线程交替打印的线程题

    背景:是这样的今天在地铁上浏览了以下网页,看到网上一朋友问了一个多线程的问题.晚上闲着没事就决定把它实现出来. 题目: 1.开启两个线程,一个线程打印A-Z,两一个线程打印1-52的数据. 2.实现交 ...

  8. Java中如何保证线程顺序执行

    只要了解过多线程,我们就知道线程开始的顺序跟执行的顺序是不一样的.如果只是创建三个线程然后执行,最后的执行顺序是不可预期的.这是因为在创建完线程之后,线程执行的开始时间取决于CPU何时分配时间片,线程 ...

  9. Java线程同步打印ABC

    需求: 三个线程,依次打印ABCABCABC.... 方案一: 使用阻塞队列,线程1从队列1获取内容打印,线程2从队列2获取内容打印,线程3从队列3中获取内容打印.线程1把B放到队列3中,线程2把C放 ...

随机推荐

  1. JavaScript: 详解正则表达式之一

    正则表达式是一个精巧的利器,经常用来在字符串中查找和替换,JavaScript语言参照Perl,也提供了正则表达式相关模块,开发当中非常实用,在一些类库或是框架中,比如jQuery,就存在大量的正则表 ...

  2. Linux之RHEL7root密码破解(二)

    破解Linux root密码的第二种方法,如下: 首先开机,进入启动界面,接着找到如下图所示的代码字段,将ro改成rw init=/sysroot/bin/sh ,如下图: 之后按“Ctrl+X”之后 ...

  3. 一些替代Xshell的软件推荐

    FinalShell: 面附上一些截图和官方连接: 官网:http://www.hostbuf.com/ FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发, ...

  4. django项目中使用手机号登录

    本文使用聚合数据的短信接口,需要先获取到申请接口的appkey和模板id 项目目录下创建ubtils文件夹,定义返回随机验证码和调取短信接口的函数 function.py文件 import rando ...

  5. Good Numbers(HDU5447+唯一分解)

    题目链接 传送门 题面 题意 首先定义对于\(k\)的好数\(u\):如果\(u\leq k\)且\(u\)的所有质因子与\(k\)的质因子一样则称\(u\)对于\(k\)是一个好数. 现给你两个数\ ...

  6. JavaWeb报错:java.sql.SQLException: Invalid value for getInt()

    1.错误描述:在对数据库进行操作时,控制台报错:java.sql.SQLException: Invalid value for getInt() :2.错误原因:数据库中表的字段的类型与实体类中属性 ...

  7. 前端知识--控制input按钮的显示与隐藏

    if(fm.ReadFlag.value=="readonly"){ var arr = document.getElementsByTagName("input&quo ...

  8. volatile 关键词

    volatile 关键字指示一个字段可以由多个同时执行的线程修改. 出于性能原因,编译器,运行时系统甚至硬件都可能重新排列对存储器位置的读取和写入. 声明了 volatile 的字段不进行这些优化.这 ...

  9. window对象(全局对象)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 004——转载—Word2016“此功能看似已中断 并需要修复”问题解决办法

    解决办法如下: 在Win10系统上安装 Office 2016 之后,每次打开Word文档可能都会提示“很抱歉,此功能看似已中断,并需要修复,请使用Windows 控制面板中的“程序和功能”选项修复M ...