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种实现方法的更多相关文章

  1. 多线程循环打印ABC

    主要是利用线程的wait()和notify()来实现 public class MyThread implements Runnable { private String name; private ...

  2. JAVA 多线程轮流打印ABC

    采用Thread+Semaphore实现,思路很简单 import java.io.IOException; import java.util.concurrent.Semaphore; public ...

  3. python 多线程实现循环打印 abc

    python 多线程实现循环打印 abc 好久没写过python了, 想自己实践一下把 非阻塞版 import threading import time def print_a(): global ...

  4. “全栈2019”Java多线程第八章:放弃执行权yield()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  5. “全栈2019”Java多线程第六章:中断线程interrupt()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java多线程第五章:线程睡眠sleep()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. 1.java多线程_实现线程的两种方式

    1.java多线程基本知识 1.1.进程介绍 不管是我们开发的应用程序,还是我们运行的其他的应用程序,都需要先把程序安装在本地的硬盘上.然后找到这个程序的启动文件, 启动程序的时候,其实是电脑把当前的 ...

  8. 【Java多线程系列三】实现线程同步的方法

    两种实现线程同步的方法 方法 特性 synchronized  不需要显式的加锁,易实现 ReentrantLock 需要显式地加解锁,灵活性更好,性能更优秀,结合Condition可实现多种条件锁  ...

  9. java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现

    注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...

随机推荐

  1. 宝塔Linux面板安装Redis

    宝塔Linux面板安装Redis不会特别麻烦,只要几步就可以实现:1.安装redis服务2.配置redis设置3.安装PHP扩展,下面就随ytkah一起来看看吧 1.首先,我们来安装redis服务,进 ...

  2. PixelRatio使用

    export default class PixelRatioView extends Component { render() { return ( <View style={styles.c ...

  3. RN TextInput用法

    效果图: 代码: import React, {Component} from 'react' import {StyleSheet, View, Text, TouchableOpacity, Te ...

  4. mysql实时增量备份

    采用binlog日志的好处 掌控所有更改操作,必要时可用于恢复数据 数据库主从复制的必要条件 [root@localhost~]# vim /etc/my.cnf [mysqld] .. .. log ...

  5. dos2unix命令

    dos2unix命令用来将DOS格式的文本文件转换成UNIX格式的(DOS/MAC to UNIX text file format converter).DOS下的文本文件是以\r\n作为断行标志的 ...

  6. kafka4 副本机制

    概述 每个分区有n个副本,可以承受n-1个节点故障. 每个副本都有自己的leader,其余都是follower. zk中存放分区的leader和 follower replica的信息.(get /b ...

  7. 并发编程---互斥锁---互斥锁与join的区别

    互斥锁 互斥锁:就是把多个进程并发,修改成一块共享数据的操作变成串行,保证是一个一个来修改的. 缺点:效率低,加锁过程复杂 优点:增加了安全性 from multiprocessing import ...

  8. PE破解win2008登录密码

    1.使用PE系统启动计算机. 2.使用cmd命令行程序. 3.备份一下magnify.exe(windows 放大镜程序). copy C:\WINDOWS\system32\magnify.exe ...

  9. 用Partimage创建或恢复分区备份

    1 Preliminary Note Partimage is part of the system rescue CD found on http://www.sysresccd.org which ...

  10. C# 抽象类、抽象属性、抽象方法

    抽象类往往用来表征对问题领域进行分析.设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象. 下面我们以水果为例,首先定义抽象类Fruit,抽象类中有公共属性vendor,抽象属 ...