Java中多个线程交替循环执行
有些时候面试官经常会问,两个线程怎么交替执行呀,如果是三个线程,又怎么交替执行呀,这种问题一般人还真不一定能回答上来。多线程这块如果理解的不好,学起来是很吃力的,更别说面试了。下面我们就来剖析一下怎么实现多个线程顺序输出。
两个线程循环交替打印
//首先我们来看一种比较简单的方式
public class ThreadCq {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
for(int i=1;i<100;i++) {
stack.add(i);
}
Draw draw = new Draw(stack);
new Thread(draw).start();
new Thread(draw).start();
}
}
class Draw implements Runnable{
private Stack<Integer> stack;
public Draw(Stack<Integer> stack) {
this.stack = stack;
}
@Override
public void run() {
while(!stack.isEmpty()) {
synchronized (this) {
notify();
System.out.println(Thread.currentThread().getName()+"---"+stack.pop());
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
这种方式是用Condition对象来完成的:
public class ThreadCq3 {
//声明一个锁
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
//创建两个Condition对象
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Stack<Integer> stack = new Stack<>();
for (int i = 0; i <= 100; i++) {
stack.add(i);
}
new Thread(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
while (true) {
lock.lock();
// 打印偶数
try {
if (stack.peek() % 2 != 0) {
c1.await();
}
System.out.println(Thread.currentThread().getName() + "-----" + stack.pop());
c2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
new Thread(() -> {
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
lock.lock();
try {
// 打印奇数
if (stack.peek() % 2 != 1) {
c2.await();
}
System.out.println(Thread.currentThread().getName() + "-----" + stack.pop());
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}).start();
}
}
这种方式是通过Semaphore来实现的:
public class ThreadCq4 {
//利用信号量来限制
private static Semaphore s1 = new Semaphore(1);
private static Semaphore s2 = new Semaphore(1);
public static void main(String[] args) {
try {
//首先调用s2为 acquire状态
s1.acquire();
// s2.acquire(); 调用s1或者s2先占有一个
} catch (InterruptedException e1) {
e1.printStackTrace();
}
new Thread(()->{
while(true) {
try {
s1.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A");
s2.release();
}
}).start();
new Thread(()->{
while(true) {
try {
s2.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
s1.release();
}
}).start();
}
}
上面就是三种比较常用的,最常用的要属第一种和第二种。
三个线程交替打印输出
上面我们看了两个线程依次输出的实例,这里我们来看看三个线程如何做呢。
public class LockCond {
private static int count = 0;
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
new Thread(()->{
while(true) {
lock.lock();
try {
while(count %3 != 0) {
//刚开始count为0 0%3=0 所以此线程执行 执行完之后 唤醒现成2,由于此时count已经进行了++,所有while成立,c1进入等待状态,其他两个也一样
c1.await();
}
System.out.println(Thread.currentThread().getName()+"========:A");
count++;
//唤醒线程2
c2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}) .start();
new Thread(()->{
while(true) {
lock.lock();
try {
while(count %3 != 1) {
c2.await();
}
System.out.println(Thread.currentThread().getName()+"========:B");
count++;
//唤醒线程3
c3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}) .start();
new Thread(()->{
while(true) {
lock.lock();
try {
while(count %3 != 2) {
c3.await();
}
System.out.println(Thread.currentThread().getName()+"========:C");
count++;
//唤醒线程1
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}) .start();
}
}
三个线程的也可以写三种,这里写一种就行了,写法和上面两个线程的都一样。大家可以自己试一下。
Condition介绍
我们在没有学习Lock之前,使用的最多的同步方式应该是synchronized关键字来实现同步方式了。配合Object的wait()、notify()系列方法可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。Object和Condition接口的一些对比。摘自《Java并发编程的艺术》

Condition接口常用方法
condition可以通俗的理解为条件队列。当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。这种方式为线程提供了更加简单的等待/通知模式。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现。
await() :造成当前线程在接到信号或被中断之前一直处于等待状态。
await(long time, TimeUnit unit) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
awaitNanos(long nanosTimeout) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
awaitUninterruptibly() :造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。
awaitUntil(Date deadline) :造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。
signal() :唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
signal()All :唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
Semaphore介绍
Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。就这一点而言,单纯的synchronized 关键字是实现不了的。他可以保证某一个资源在一段区间内有多少给线程可以去访问。


从源码我们可以看出来,它new了一个静态内部类,继承Sync接口。他同时也提供了一些构造方法

比如说通过这个构造方法可以创建一个是否公平的Semaphore类。关于公平锁和非公平锁大家可以看我的另外一篇文章。
关于这块更多的了解可以看我其他的文章去了解。
有问题可以在下面评论,技术问题可以私聊我。
Java中多个线程交替循环执行的更多相关文章
- 使用Java实现三个线程交替打印0-74
使用Java实现三个线程交替打印0-74 题目分析 三个线程交替打印,即3个线程是按顺序执行的.一个线程执行完之后,唤醒下一个线程,然后阻塞,等待被该线程的上一个线程唤醒.执行的顺序是一个环装的队列 ...
- Java中的守护线程 & 非守护线程(简介)
Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...
- Java中如何创建线程
Java中如何创建线程 两种方式:1)继承Thread类:2)实现Runnable接口. 1.继承Thread类 继承Thread类,重写run方法,在run方法中定义需要执行的任务. class M ...
- Java并发 两个线程交替执行和死锁
今天看到一个题:两个线程交替打印奇数和偶数,即一个线程打印奇数,另一个打印偶数,交替打印从1到100.想了下有多重实现方法. wait和notify方法: public class OddEven { ...
- java中等待所有线程都执行结束(转)
转自:http://blog.csdn.net/liweisnake/article/details/12966761 今天看到一篇文章,是关于java中如何等待所有线程都执行结束,文章总结得很好,原 ...
- java中等待所有线程都执行结束
转自:http://blog.csdn.net/liweisnake/article/details/12966761 今天看到一篇文章,是关于java中如何等待所有线程都执行结束,文章总结得很好,原 ...
- Java中如何保证线程顺序执行
只要了解过多线程,我们就知道线程开始的顺序跟执行的顺序是不一样的.如果只是创建三个线程然后执行,最后的执行顺序是不可预期的.这是因为在创建完线程之后,线程执行的开始时间取决于CPU何时分配时间片,线程 ...
- JAVA中等待所有线程都执行结束(转2)
场景: package com.java4all.mypoint; import java.util.concurrent.CountDownLatch; public class ThreadTes ...
- Java 中如何实现线程间通信
世界以痛吻我,要我报之以歌 -- 泰戈尔<飞鸟集> 虽然通常每个子线程只需要完成自己的任务,但是有时我们希望多个线程一起工作来完成一个任务,这就涉及到线程间通信. 关于线程间通信本文涉及到 ...
随机推荐
- 以gnome-terminal为例,修改gnome3 的默认配置
gnome连续几个版本的terminal默认配置文件都是同一个配置文件“b1dcc9dd-5262-4d8d-a863-c897e6d979b9”.这是因为gnome的developers编辑了这个配 ...
- Vim常用快捷键--正常的学习曲线
vim可能对于初学者不太友好,学习曲线有点陡,特此整理了较为平滑的学习曲线的学习快捷键的方式,包含最常用的快捷键,让初学者领悟vim的优点,想要进阶学习请查找其它更好的教程 正常模式:可以使用快捷键命 ...
- Linux命令学习(2): scp和rsync基本用法与断点续传
版权声明:本文为博主原创文章,未经允许不得转载. 引子 在平常的工作中,我经常需要在远程服务器和本地之间传输文件. 以前我都使用scp命令,直到今天因为网络中断,scp出现了stalled. 因为上传 ...
- 框架之---Flask
Flask是一个轻量级的WEB框架,它和Django相比,就是一个胖子和一个骨架的区别. Flask是一个py文件就可以run的web框架,但是就因为是py文件就能run,所有,啥都没有,等把Flas ...
- axios在实际项目中的使用介绍
1.axios本身就封装了各种数据请求的方法 执行 GET 请求 // 为给定 ID 的 user 创建请求 axios.get('/user?ID=12345') .then(function (r ...
- layui laypage 当前页刷新问题
困扰了好几天的问题,终于找到答案了 在执行完代码后添加下面的代码实现当前页的刷新 $(".layui-laypage-btn").click(); 在解决问题的过程中,其实已经注意 ...
- Apache Maven Cookbook(一)maven 使用命令创建、编译java项目
一.创建 使用命令创建项目分几步: 1.打开命令行窗口,比如cmd,把目录切换至想要创建项目地方. 2.执行如下命令: mvn archetype:generate -DgroupId=com.zua ...
- 【04】AngularJS 表达式
AngularJS 表达式 AngularJS 使用 表达式 把数据绑定到 HTML. AngularJS 表达式 AngularJS 表达式写在双大括号内:{{ expression }}. Ang ...
- Poor Hanamichi
Poor Hanamichi Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- [K/3Cloud] 表单python脚本使用QueryService的做法
听说有些朋友想在表单里做自定义的界面数据处理,一般来说写个表单插件会很容易解决这类问题.但是鉴于C#插件开发的不便性和实施搭建开发环境的麻烦,在现场做C#开发可能会不太方便(没部署开发环境之类的问题) ...