Java多线程循环打印ABC的5种实现方法
Posted 大浪不惊涛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程循环打印ABC的5种实现方法相关的知识,希望对你有一定的参考价值。
题目: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();
}
}
}
}
原始文地址:https://blog.csdn.net/weixin_39723337/article/details/80352783
以上是关于Java多线程循环打印ABC的5种实现方法的主要内容,如果未能解决你的问题,请参考以下文章