场景

编写一个程序,启动三个线程,三个线程的name分别是A,B,C;,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC...

使用 synchronized 实现

public class MyService
{
private int flag = 1; public synchronized void printA(){ while (flag != 1)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
flag = 2;
this.notifyAll();
}
public synchronized void printB(){
while (flag != 2)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
flag = 3;
this.notifyAll();
}
public synchronized void printC(){
while (flag != 3)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
flag = 1;
this.notifyAll();
}
}

这里的判断条件中用的是 while 而不是 if , 这两者之间有什么区别呢? 线程从 wait 状态被唤醒,并且获得锁以后会继续往下执行,比如 A 调用nofityAll() 唤醒 B,C,这时 B与C谁会先获得锁是不确定的。如果是C先获得了锁,那么C就继续往下执行打印,这与我们的期望的不符。所以这里我们使用了一个 while,当C获得锁以后再去判断一下flag,如果这时还不是它执行的时候,它就再次进入wait状态。此时A与C都是wait状态,获得锁的一定是B,从而实现我们期望的顺序打印。

测试类

package testABC;

public class TestMain
{
public static void main(String[] args)
{
//编写一个程序,启动三个线程,三个线程的ID分别是A,B,C;,每个线程将自己的ID值在屏幕上打印5遍,打印顺序是ABCABC...
// MyService service = new MyService();
MyService2 service = new MyService2(); Thread A = new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
service.printA();
}
}
});
A.setName("A");
Thread B = new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
service.printB();
}
}
});
B.setName("B");
Thread C = new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
service.printC();
}
}
});
C.setName("C"); A.start();
B.start();
C.start();
}
}

使用 Lock 实现

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class MyService2
{
private int flag = 1;
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition(); public void printA()
{
try
{
lock.lock();
if (flag != 1)
{
try
{
conditionA.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
flag = 2;
conditionB.signal();
}
finally
{
lock.unlock();
} } public void printB()
{
try
{
lock.lock();
if (flag != 2)
{
try
{
conditionB.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
flag = 3;
conditionC.signal();
}
finally
{
lock.unlock();
} } public void printC()
{
try
{
lock.lock();
if (flag != 3)
{
try
{
conditionC.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.print(Thread.currentThread().getName());
flag = 1;
conditionA.signal();
}
finally
{
lock.unlock();
}
}
}

当使用LOCK时可以不使用while因为condition可以唤醒指定的线程。同时注意必须先调用 conditionA.signal(); 再调用 lock.unlock(); ,否则会抛 java.lang.IllegalMonitorStateException 异常。因为在调用unlock之后,当前线程已不是此监视器对象condition的持有者。也就是说要在此线程持有锁定对象时,才能使用此锁定对象。

关于此异常的博文:关于java.lang.IllegalMonitorStateException

java 多线程 实现多个线程的顺序执行的更多相关文章

  1. Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

    一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...

  2. Java多线程(一) —— 线程的状态详解

    一.多线程概述  1. 进程 是一个正在执行的程序.是程序在计算机上的一次运行活动. 每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元. 系统以进程为基本单位进行系统资源的调度 ...

  3. Java多线程(五)线程的生命周期

    点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...

  4. Java多线程-同步:synchronized 和线程通信:生产者消费者模式

    大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...

  5. C#之使用AutoResetEvent实现线程的顺序执行

    前几天一朋友问我如何实现线程的顺序执行,说真的,虽然看过CLR这本书,也把线程部分拜读了两遍,但是这个问题出来之后还是没有一个思路.今天在搜索资料的时候无意中再次看到AutoResetEvent这个东 ...

  6. Qt 控制线程的顺序执行(使用QWaitCondition,并且线程类的run函数里记得加exec(),使得线程常驻)

    背景项目中用到多线程,对线程的执行顺序有要求: A.一个线程先收数据 B.一个线程处理数据 C.一个线程再将处理后的数据发送出去 要求三个线程按照ABC的顺序循环执行. 思路子类化多线程方法 重写子类 ...

  7. Java多线程——<三>简单的线程执行:Executor

    一.概述 按照<Java多线程——<一><二>>中所讲,我们要使用线程,目前都是显示的声明Thread,并调用其start()方法.多线程并行,明显我们需要声明多个 ...

  8. java多线程(1)---线程创建、start、run

    线程创建.start.run 一.创建线程方式 java创建线程的方式,主要有三种:类Thread.接口Runnable.接口Callable. 1.Thread和Runnable进行比较 他们之间的 ...

  9. java多线程系列六、线程池

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...

随机推荐

  1. LiberOJ #124. 除数函数求和 【整除分块】

    一.题目 #124. 除数函数求和 二.分析 比较好的一题,首先我们要对题目和样例进行分析,明白题目的意思. 由于对于每一个$d$,它所能整除的数其实都是定的,且数量是$ \lfloor \frac{ ...

  2. Vue 全家桶学习资源(转)

    companion: React 全家桶学习资源(持续更新) 下面整理了一些关于Vue以及Vue衍生的学习资源: 官网文档 官网API ECMAScript 6 入门 30分钟掌握ES6/ES2015 ...

  3. find文本处理(locate)实例学习记录

    find文本处理(locate)实例学习记录 (一)按文件名称查找 按照文件名称查找是 find 最常见的用法,需要注意的是,搜索的文件名必须完全匹配,才能找到对应的文件. 1. 查找当前目录下所有 ...

  4. P1090 合并果子(JAVA语言)

    题目描述 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可 ...

  5. java例题_08 输入特定数字求和(n个a位数递增求和问题)

    1 /*8 [程序 8 输入数字求和] 2 题目:求 s=a+aa+aaa+aaaa+aa...a 的值,其中 a 是一个数字.例如 2+22+222+2222+22222(此时共有 5 个数相加), ...

  6. SFDC Trigger里before和after的区别

    最近项目开始用Trigger来进行Validation Check.也知道可以通过配置Object里的Validation Rule来进行Check,但是项目想如果有Trigger里就都在Trigge ...

  7. Starting Tomcat v9.0 Server at localhost' has encountered a problem

    •问题描述 在通过 Eclipse 打开 Tomcat 时报错: •解决方案 找到 Tomcat 的安装位置,打开 tomcat\bin 目录,找到 shutdown.bat,手动关闭 tomcat: ...

  8. 手机浏览器通过Scheme跳转APP,兼容各种手机浏览器

    一个比较完整的产品线,必定有APP和网站,另外还有微信公众号网页和小程序.那么有一个比较常见的需求就是在手机浏览器内打开APP,实现起来也比较简单,只要APP配置的有URLScheme即可. 但是因为 ...

  9. 获取执行计划之Autotrace

    Autotrace 简介 AUTOTRACE是一项SQL*Plus功能,自动跟踪为SQL语句生成一个执行计划并且提供与该语句的处理有关的统计. AUTOTRACE的好处是您不必设置跟踪文件的格式,并且 ...

  10. k8s:py项目发布完整流程

    k8s:py项目发布流程 1. 编写Dockerfile # cat Dockerfile FROM python:3.6-slim USER root RUN apt-get update & ...