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 中如何实现线程间通信
世界以痛吻我,要我报之以歌 -- 泰戈尔<飞鸟集> 虽然通常每个子线程只需要完成自己的任务,但是有时我们希望多个线程一起工作来完成一个任务,这就涉及到线程间通信. 关于线程间通信本文涉及到 ...
随机推荐
- python使用xlrd和xlwt读写Excel文件
版权声明:本文为博主原创文章,未经允许不得转载. 安装模块 如果使用的是Linux系统,并且安装了pip,可以直接使用pip安装xlrd, xlwt: pip install xlwt pip ins ...
- gulp-file-include 合并 html 文件
gulp-file-include 是 gulp 插件,它提供了一个 include 方法让我们可以像后端模板那样把公共部分的页面导入进来. 安装依赖包(包括了 gulp-file-include 和 ...
- 解决window 10 安装软件2503 2502错误
1.首先打开任务管理器,可以通过右键点击桌面上的任务栏打开任务管理器,也可以通过同时按下键盘上的Ctrl+Alt+Delete键打开任务管理器. 2.打开任务管理器后,切换到“详细信息”选项卡,找到e ...
- mac下安装好jdk和jmeter后设置环境变量
1. 执行vim ~/.bash_profile,打开文件: 2. 按i,进入输入状态,并输入如下信息,其中为jdk安装路径: export JAVA_HOME=/Library/Java/JavaV ...
- day21 01 包的初识
day21 01包的初识 包:把解决一类问题的模块放在同一个文件夹里面-----包(一个包里面通常会含有_init_.py文件(python2里面必须有),但是后面的就没有要求一定要有了) 同样导入的 ...
- Ural 1036 Lucky Tickets
Lucky Tickets Time Limit: 2000ms Memory Limit: 16384KB This problem will be judged on Ural. Original ...
- 零基础到架构师 不花钱学JavaEE(基础篇)- 概述
Java简单来说是一门语言,Java能干什么? 网站:开发大,中,小型网站. 服务器端程序:企业级程序开发. APP:Android的APP基本使用Java开发. 云:Hadoop就是使用Java语言 ...
- [K/3Cloud]K3Cloud平台开发之Python插件
有时候我们的表单可能很简单,只是一个简单交互的表单,但有可能还是要劳师动众的给它建个工程写个插件,是不是很不爽?例如我有如下一个表单: 功能很简单就是选个业务对象,收集绑定几个字段名,然后确定返回一个 ...
- java 中public 类
java 中的文件名是以这个文件里面的public 的那个类命名的(也就是说文件名和这个文件里面的那个public 属性的class 名称一样), 同一个文件中不能放多个(超过2个)的pulic 类. ...
- Caused by: java.lang.ClassNotFoundException: org.springframework.boot.context.embedded.FilterRegistrationBean
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.context.embedded.FilterRegistr ...