Java多线程循环打印ABC的5种实现方法
https://blog.csdn.net/weixin_39723337/article/details/80352783
题目:3个线程循环打印ABC,其中A打印3次,B打印2次,C打印1次,循环打印2轮
一.Synchronized同步法
思路:使用synchronized、wait、notifyAll的方法利用线程标记变量控制三个线程的执行顺序。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
int num = 1;//当前正在执行线程的标记
ABCPrint print = new ABCPrint(num);
Thread threadA = new Thread(new RunnableA(print));
Thread threadB = new Thread(new RunnableB(print));
Thread threadC = new Thread(new RunnableC(print));
threadA.start();
Thread.sleep(500);
threadB.start();
Thread.sleep(500);
threadC.start();
}
}
class RunnableA implements Runnable{
private ABCPrint print;
public RunnableA(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintA();
}
}
class RunnableB implements Runnable{
private ABCPrint print;
public RunnableB(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintB();
}
}
class RunnableC implements Runnable{
private ABCPrint print;
public RunnableC(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintC();
}
}
class ABCPrint {
private int num;//当前正在执行线程的标记
public ABCPrint(int num) {
super();
this.num = num;
}
public void PrintA(){
for (int j = 0; j < 2; j++)//表示 循环打印2轮
synchronized(this){
while(num != 1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 3; i++) {//表示 打印3次
System.out.println("A");
}
//打印A线程执行完 ,通知打印B线程
num = 2;
this.notifyAll();
}
}
public void PrintB(){
for (int j = 0; j < 2; j++)//表示 循环打印2轮
synchronized(this){
while(num != 2){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 2; i++) {//表示 打印2次
System.out.println("B");
}
//打印B线程执行完 ,通知打印C线程
num = 3;
this.notifyAll();
}
}
public void PrintC(){
for (int j = 0; j < 2; j++)//表示 循环打印2轮
synchronized(this){
while(num != 3){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("C");
//打印C线程执行完 ,通知打印A线程
num = 1;
this.notifyAll();
}
}
}
二.Lock锁方法
思路:Lock锁机制是JDK 5之后新增的锁机制,不同于内置锁,Lock锁必须显式声明,并在合适的位置释放锁。Lock是一个接口,通过ReentrantLock具体实现进行显式的锁操作,即获取锁和释放锁。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
int num = 1;//当前正在执行线程的标记
ABCPrint print = new ABCPrint(num);
Thread threadA = new Thread(new RunnableA(print));
Thread threadB = new Thread(new RunnableB(print));
Thread threadC = new Thread(new RunnableC(print));
threadA.start();
Thread.sleep(500);
threadB.start();
Thread.sleep(500);
threadC.start();
}
}
class RunnableA implements Runnable{
private ABCPrint print;
public RunnableA(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintA();
}
}
class RunnableB implements Runnable{
private ABCPrint print;
public RunnableB(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintB();
}
}
class RunnableC implements Runnable{
private ABCPrint print;
public RunnableC(ABCPrint print) {
super();
this.print = print;
}
@Override
public void run() {
print.PrintC();
}
}
class ABCPrint {
private static final Lock lock = new ReentrantLock();//通过JDK5中的Lock锁来保证线程的访问的互斥
private int num;//当前正在执行线程的标记
public ABCPrint(int num) {
super();
this.num = num;
}
public void PrintA(){
for (int j = 0; j < 2;)//表示 循环打印2轮
try {
lock.lock();
while(num == 1){
for (int i = 0; i < 3; i++) {//表示 打印3次
System.out.println("A");
}
//打印A线程执行完 ,通知打印B线程
num = 2;
j++;
}
}finally{//调用了lock方法后,需在finally(finally确保一定会执行,除非执行了exit方法)语句里调用unlock方法。否则会造成死锁等问题
lock.unlock();
}
}
public void PrintB(){
for (int j = 0; j < 2;)//表示 循环打印2轮
try{
lock.lock();
while(num == 2){
for (int i = 0; i < 2; i++) {//表示 打印2次
System.out.println("B");
}
//打印B线程执行完 ,通知打印C线程
num = 3;
j++;
}finally{
lock.unlock();
}
}
public void PrintC(){
for (int j = 0; j < 2;)//表示 循环打印2轮
try{
lock.lock();
while(num == 3){
System.out.println("C");
//打印C线程执行完 ,通知打印A线程
num = 1;
j++;
}
}finally{
lock.unlock();
}
}
}
三.ReentrantLock结合Condition
思路:Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法。Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
/**
* @author XDarker
* 2018-5-17
*/
public class Main{
public static void main(String[] args) {
final AlternateDemo ad = new AlternateDemo();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
ad.loopA(i);
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
ad.loopB(i);
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
ad.loopC(i);
}
}
},"C").start();
}
}
class AlternateDemo{
private int num = 1;//当前正在执行线程的标记
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void loopA(int loop){
lock.lock();
try {
//1.判断
if(num != 1){
condition1.await();
}
//2.打印
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName()+"-"+"第"+loop+"轮"+"-第"+i+"次");
}
//3.唤醒
num = 2;
condition2.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
public void loopB(int loop){
lock.lock();
try {
//1.判断
if(num != 2){
condition2.await();
}
//2.打印
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName()+"-"+"第"+loop+"轮"+"-第"+i+"次");
}
//3.唤醒
num = 3;
condition3.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
public void loopC(int loop){
lock.lock();
try {
//1.判断
if(num != 3){
condition3.await();
}
//2.打印
for (int i = 0; i < 1; i++) {
System.out.println(Thread.currentThread().getName()+"-"+"第"+loop+"轮"+"-第"+i+"次");
}
System.out.println("---------------------------");
//3.唤醒
num = 1;
condition1.signal();
} catch (Exception e) {
// TODO: handle exception
}finally{
lock.unlock();
}
}
}
四.AtomicInteger方法
思路:AtomicInteger,一个提供原子操作的Integer的类。在Java中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) {
new ABCPrint("A",3).start();//A打印3次
new ABCPrint("B",2).start();//B打印2次
new ABCPrint("C",1).start();//C打印1次
}
}
class ABCPrint extends Thread {
//打印次数
private int count;
private final String str[] = { "A", "B", "C" };
private final static AtomicInteger atomCount= new AtomicInteger();
public ABCPrint(String name,int count) {
this.setName(name);
this.count = count;
}
@Override
public void run() {
while (true) {
// 循环满2轮退出打印
if (atomCount.get() / 3 == 2) {
break;
}
synchronized (atomCount) {
// 顺序打印A、B、C
if (str[atomCount.get() % 3].equals(getName())) {
atomCount.getAndIncrement();//自增
//对应打印几次
for (int i = 0; i < count; i++) {
System.out.println(getName());
}
//表示一轮打印结束 方便观察打印下分隔符
if ("C".equals(getName())) {
System.out.println("================================");
}
// 当前线程打印打印完成后唤醒其它线程
atomCount.notifyAll();
} else {
// 非顺序线程wait()
try {
atomCount.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
五.Semaphore信号量方式
思路: 信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。Semaphore线程同步机制,当调用acquire()时,内部计数器数值增加;调用release()时,内部计数器递减;计数器值不能小于0,如果等于0,acquire()方法被阻塞,需要等待其他线程调用release()方法。
/**
* @author XDarker
* 2018-5-17
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
new ThreadA(3).start();
new ThreadB(2).start();
new ThreadC(1).start();
}
//以A开始的信号量,初始信号量数量为1
private static Semaphore A = new Semaphore(1);
//B、C信号量,A完成后开始,初始信号数量为0
private static Semaphore B = new Semaphore(0);
private static Semaphore C = new Semaphore(0);
static class ThreadA extends Thread {
private int count;
public ThreadA(int count) {
super();
this.count = count;
}
@Override
public void run() {
try {
for (int i = 0; i < 2; i++) {
A.acquire();// A获取信号执行,A信号量减1,当A为0时将无法继续获得该信号量
for (int j = 0; j < count; j++) {
System.out.print("A");
}
B.release();// B释放信号,B信号量加1(初始为0),此时可以获取B信号量
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadB extends Thread {
private int count;
public ThreadB(int count) {
super();
this.count = count;
}
@Override
public void run() {
try {
for (int i = 0; i < 2; i++) {
B.acquire();
for (int j = 0; j < count; j++) {
System.out.print("B");
}
C.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ThreadC extends Thread {
private int count;
public ThreadC(int count) {
super();
this.count = count;
}
@Override
public void run() {
try {
for (int i = 0; i < 2; i++) {
C.acquire();
for (int j = 0; j < count; j++) {
System.out.println("C");
}
A.release();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
---------------------
作者:XDarker
来源:CSDN
原文:https://blog.csdn.net/weixin_39723337/article/details/80352783
版权声明:本文为博主原创文章,转载请附上博文链接!
Java多线程循环打印ABC的5种实现方法的更多相关文章
- 多线程循环打印ABC
主要是利用线程的wait()和notify()来实现 public class MyThread implements Runnable { private String name; private ...
- JAVA 多线程轮流打印ABC
采用Thread+Semaphore实现,思路很简单 import java.io.IOException; import java.util.concurrent.Semaphore; public ...
- python 多线程实现循环打印 abc
python 多线程实现循环打印 abc 好久没写过python了, 想自己实践一下把 非阻塞版 import threading import time def print_a(): global ...
- “全栈2019”Java多线程第八章:放弃执行权yield()方法详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第六章:中断线程interrupt()方法详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第五章:线程睡眠sleep()方法详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- 1.java多线程_实现线程的两种方式
1.java多线程基本知识 1.1.进程介绍 不管是我们开发的应用程序,还是我们运行的其他的应用程序,都需要先把程序安装在本地的硬盘上.然后找到这个程序的启动文件, 启动程序的时候,其实是电脑把当前的 ...
- 【Java多线程系列三】实现线程同步的方法
两种实现线程同步的方法 方法 特性 synchronized 不需要显式的加锁,易实现 ReentrantLock 需要显式地加解锁,灵活性更好,性能更优秀,结合Condition可实现多种条件锁 ...
- java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现
注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...
随机推荐
- MongoDB的客户端管理工具--nosqlbooster 查询工具使用
连接我的MongoDB 看到这样 打开db1数据库里面user集合,看到user集合里面的数据,他会自带查询语句 看这里以tree方式显示 可以以table方式显示 还可以json方式显示 按照自己的 ...
- 20170719 Mysql 配置远端Mysql访问,增加表/存储过程
-- 1 .在windows 环境中安装Mysql 会按照到默认的C盘当中,如何修改呢--? -- 2. 如何只安装客户端不安装Mysql 数据库服务 --? -- 3. 表的特殊列,默认采用函数值 ...
- 小睿开始呼叫用户,然后FS怎么跟用户交互的整个流程原理
学习从小睿开始呼叫用户,然后FS怎么跟用户交互的整个流程原理; 1.小睿向欣方新发起呼叫请求; 2.欣方新可以通过线路发起SIP协议请求,来呼叫用户; 3.当用户接通后,将建立 ...
- Centos7限速和测速
限速 wondershaper是国外人开发的一款在Linux内核下基于TC工具的对整块网卡的限度工具. 第一种安装方法 首先下载wondershaper的rpm安装包:wondershaper-1.1 ...
- Spark Sql之ThriftServer和Beeline的使用
概述 ThriftServer相当于service层,而ThriftServer通过Beeline来连接数据库.客户端用于连接JDBC的Server的一个工具 步骤 1:启动metastore服务 . ...
- [django]python异步神器-celery
python异步神器celery https://segmentfault.com/a/1190000007780963
- MySQL 基础 事务
什么是mysql的事务 MySQL 事务主要用于处理操作量大,复杂度高的数据.简单的说,事务就是一连串的DML的sql语句组合在一起,所以语句执行成功才算成功,如果有语句执行失败,执行就不成功 .比如 ...
- 天气服务API文档 第1版
HTTP接口设计文档 此文档为开发HTTP接口的设计文档,目前用于提供天气查询的相关接口. 测试的时候使用 URL=http://www.dennisthink.com/test/api/weathe ...
- xcode 报错Failed to load project at xxxx ,incompatible project version
错误原因: 由于工程是低版本的Xcode建立的,在使用高版本的Xcode打开时会出现编译不了工程. 解决方法: 鼠标右击.xcodeproj文件 —>显示包内容 —>打开project.p ...
- 一个基于JRTPLIB的轻量级RTSP客户端(myRTSPClient)——实现篇:(十)使用JRTPLIB传输RTP数据
myRtspClient通过简单修改JRTPLIB的官方例程作为其RTP传输层实现.因为JRTPLIB使用的是CMAKE编译工具,这就是为什么编译myRtspClient时需要预装CMAKE. 该部分 ...