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 ...
随机推荐
- bat、sh等批处理文件(脚本文件)
批处理文件(batch file):也被称为批处理程序或脚本,可以简化日常或重复性任务.本质是无格式的文本文件,它包含一条或多条命令.(1).bat是dos下的批处理文件,在window系统上执行的文 ...
- OpenFace的一些了解
1.OpenFace内4个样例代码 配置学习了两个 其一: Ubantu 基本命令 Docker 安装方式.发布网站方式.查看验证安装结果命令 Openface 基本demo 实现方式.和基本原理 其 ...
- 火币网API文档——WebSocket API错误码
错误信息返回格式 { "id": "id generate by client", "status": "error", ...
- Python3学习之路~3.1 函数基本语法及特性、返回值、参数、局部与全局变量
1 函数基本语法及特性 定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可 特性: 减少重复代码 使程序变的可扩展 使程序变得易维护 语法定义: d ...
- wx:Textctrl
import wx class Mywin(wx.Frame): def __init__(self, parent, title): super(Mywin, self).__init__(pare ...
- vux 使用swiper 垂直滚动文字 报错[Intervention] Ignored...
[Intervention] Ignored attempt to cancel a touchmove event with cancelable=false, for example becaus ...
- 让运行着的ASP.NET Web程序重新启动
在运行中的Web程序,会随时的监测这web.config文件的变化,只要web.config文件有变动,那么Application就会重新启动.所以,在一个工具类中我们可以封装一个方法,该方法用来重新 ...
- Kotlin Linux下的环境搭建
Kotlin是JVM世界里的未来主宰,不管你信不信,我反正相信了! 好吧,如果你不糊涂,就跟着我一起学学吧,哈哈 一下载https://github.com/JetBrains/kotlin/rele ...
- 阿里云服务器用smtp发送邮件返失败
阿里云使用SMTP发送邮件失败,因为阿里云服务器屏蔽了25端口,所以发送不成功,解决办法改用587发送QQ邮件,且必须使用SSL,否则不成功. 经测试QQ的465,995不能使用. https://b ...
- vue中定时器的使用方式
就这么搞定 no no no 离开页面的时候还必须清楚定时器