zoukankan      html  css  js  c++  java
  • 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
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    对于数据的测试
    绕过前端,直接调用后端接口的可能性
    API接口自动化之3 同一个war包中多个接口做自动化测试
    API接口自动化之2 处理http请求的返回体,对返回体做校验
    API接口自动化之1 常见的http请求
    DB中字段为null,为空,为空字符串,为空格要怎么过滤取出有效值
    Linux 常用的压缩命令有 gzip 和 zip
    SQL 常用的命令
    JVM内存管理的机制
    Linux 常见命令
  • 原文地址:https://www.cnblogs.com/yaowen/p/10103196.html
Copyright © 2011-2022 走看看