场景

编写一个程序,启动三个线程,三个线程的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. 2018ICPC南京D. Country Meow

    题目: 题意:三维里有n个点,找一个最小的球将所有点覆盖. 题解:退火法模拟的一道板子题. 1 #include <stdio.h> 2 #include <iostream> ...

  2. SQL注入与参数化查询

    SQL注入的本质 SQL注入的实质就是通过SQL拼接字符串追加命令,导致SQL的语义发生了变化.为什么发生了改变呢? 因为没有重用以前的执行计划,而是对注入后的SQL语句重新编译,然后重新执行了语法解 ...

  3. (原创)在Linux上安装运行Python3(CentOS7为例)

    在win10上开发好的python项目要部署在Linux上要面对的问题:怎么在Linux上跑py文件呢? 以Lunix CentOS7.x平台为例,CentOS系统上自带的已有python2.x 的版 ...

  4. Android学习之RecyclerView初探究

    •RecyclerView基本用法 RecyclerView是新增的控件,为了让 RecyclerView 在所有 Android 版本上都能使用; Android 团队将 RecyclerView ...

  5. 基于react hooks,zarm组件库配置开发h5表单页面

    最近使用React Hooks结合zarm组件库,基于js对象配置方式开发了大量的h5表单页面.大家都知道h5表单功能无非就是表单数据的收集,验证,提交,回显编辑,通常排列方式也是自上向下一行一列的方 ...

  6. 围绕 Kubernetes 的 8 大 DevOps 生产关键实践

    本文主要介绍 DevOps 的 8 大关键实践在 Kubernetes 平台下如何落地,结合我们目前基于 Kubernetes 平台的 DevOps 实践谈谈是如何贯彻相关理念的,这里不会对其具体实现 ...

  7. Python基础(七):字符串的使用(上)

    什么是字符串? 字符串该如何定义呢?通俗的说:字符串就是一系列的字符. 创建字符串的3种方式 单引号创建 >>> a = 'I am a student' >>> ...

  8. 【OO第二次作业】关于Homework2性能分的思考

    为了获得更多的性能分,个人目前想到了以下注意点: Homework2与Homework1还是有不少相似之处的,因此在上次作业中的这些性能提升点,此次依然有效-- 表达式之间不含空格 如果存在正项,则表 ...

  9. 恒骊学堂的JAVA初步学习总纲--转载

  10. HMS Toolkit助力开发者高效集成HMS Core

    当你的应用想集成华为HMS Core服务和上线华为应用市场,或当你已经开发了一个Android应用并集成了第三方移动服务,需要迁移使用HMS Core服务和上线华为应用市场的时候,如何快速.便捷.高效 ...