如何实现有三个线程a,b,c,这三个线程都有一个方法分别打印A、B、C,问怎么能实现依次打印ABC的功能

一、今天有家电面(具体哪家就不提了)问了这样的问题:

如何实现有三个线程a,b,c,这三个线程都有一个方法分别打印A、B、C,问怎么能实现依次打印ABC的功能。当时想着使用优先级或者join方法(肯定不行,因为你定的优先级不太听话不会按照你定的去依次执行,join方法去抢占其它线程的时间片也不好使,但是,去抢占主线程的时间片是好使的),其实就是利用线程池去解决这个问题:

1.思路:其实就是让三个线程依次运行,运用的是线程池来完成的,创建一个只有一个线程的线程池来操作不限数量的队列,也就是把线程都放在一个队列中,队列我们都知道是先进先出的,也就是说这个线程池中的线程其实真正工作的只有一个,其它队列中的线程都处于休眠状态(也就是sleep状态),当这个线程结束之后,也就是run方法结束之后,又从队列中拿出一个休眠的线程来唤醒,这样依次把队列中的所有线程处理完毕,这样这个线程池的线程并没有真正结束,如果线程池中没有待处理的线程,这个线程池会一直等待,等待下一次任务的提交,除非把线程池给shutdown掉,这样这个线程池的生命周期才算结束。

代码如下:

/**
 * 我们要做的就是有三个线程a,b,c,这三个线程都有一个方法分别打印A、B、C,问怎么能实现依次打印ABC的功能
 * @author zhmm
 */
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadShunXu {
	public static void main(String[] args) {
		 //创建三个线程,a b c分别打印A B C三个字符
		 Thread a = new Thread(new Runnable() {
	            public void run() {
	                System.out.println(Thread.currentThread().getName() + "A");
	            }
	     });
		 
	     Thread b = new Thread(new Runnable() {
	            public void run() {
	                System.out.println(Thread.currentThread().getName() + "B");
	            }
	     });
	     
	     Thread c = new Thread(new Runnable() {
	            public void run() {
	                System.out.println(Thread.currentThread().getName() + "C");
	            }
	     });
	     //创建一个线程池,把这个三个线程装进这个线程池里
	     //线程池的实现是用的队列这个数据结构,因此先进先出,且每次只能弹出一个线程
	     //其实就是利用线程池完成每次工作的线程只有一个,且是队头线程
	     ExecutorService executor = Executors.newSingleThreadExecutor();
	     executor.submit(a);
	     executor.submit(b);
	     executor.submit(c);
	     //结束该线程池的生命周期
	     executor.shutdown();
	        
	}
}

运行结果:


2.使用线程的join方法区强占mian方法的时间片,导致主线程只能等待当前线程运行完毕之后才能继续往下运行,其它线程才能执行,从而达到依次执行三个线程,并依次打印ABC的目的。

/**
 * @author zhmm
 */
public class PrintABC_1 {
    public static void main(String[] args) throws InterruptedException {
        while(true) {
            A a1 = new A();
            B b1 = new B();
            C c1 = new C();
            Thread a = new Thread(a1);
            Thread b = new Thread(b1);
            Thread c = new Thread(c1);
            a.start();
            a.join();
            b.start();
            b.join();
            c.start();
            c.join();
        }
    }
}
class A implements Runnable {
    @Override
    public void run() {
        System.out.println("A");
    }
}
class B implements Runnable {
    @Override
    public void run() {
        System.out.println("B");
    }
}
class C implements Runnable {
    @Override
    public void run() {
        System.out.println("C");
    }
}

那有人说,在其它线程里去抢占别的线程的时间片行不行,在一轮打印,而且不限制线程必须依次执行的时候是行的。

/**
 * @author zhmm
 * 此种方法只能保证一次顺序打印ABC,无法做到线程ABC顺序执行和循环打印
 * 无法做到线程ABC顺序执行显而易见,就是使用join方法去抢占其它线程的时间片,肯定其它线程已经运行了
 * 无法做到循环打印是因为,左下每一组中的线程仍然相互依赖,依次打印,但是不同组线程之间并没有依赖关系,因此无法保证顺序执行
 *
 * 使用join方法去解决这个问题也是可以的,不过是抢占的主线程的时间片
 */
public class PrintShunXABC {
    public static void main(String[] args) {
            ThreadA threadA = new ThreadA();
            ThreadB threadB = new ThreadB(threadA);
            ThreadC threadC = new ThreadC(threadB);
            threadA.start();
            threadB.start();
            threadC.start();

    }
}
class ThreadA extends Thread {
    @Override
    public void run() {

        System.out.println("A");


    }
}

class ThreadB extends Thread {
    private ThreadA threadA;

    public ThreadB(ThreadA threadA) {
        this.threadA = threadA;
    }

    @Override
    public void run() {
        try {
            threadA.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("B");

    }
}

class ThreadC extends Thread {
    private ThreadB threadB;

    public ThreadC(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            threadB.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("C");

    }

}

但是,当执行许多次的时候是不行的,读者在mian方法中加个循环即可验证。


二、有三个方法A、B、C分别打印A、B、C这三个字符串,怎样增做吧这三个字符串按ABC依次打印

其实这道题和上面的一样,也可以使用线程池中的堵塞队列来完成,但是也可以使用锁+标记数+ wait() 和 notify() 方法来完成对线程的进行等待和唤醒。

逻辑思路:

这里主要用到了两个内容

1)创建一个标记 flag,让程序进行判断:

当flag != 1 时,A方法 进入等待,否则,执行方法flag=2,唤醒所有正在等待的其它线程;

当flag != 2 时,A方法 进入等待,否则,执行方法flag=2,唤醒所有正在等待的其它线程;

当flag != 3 时,A方法 进入等待,否则,执行方法flag=2,唤醒所有正在等待的其它线程。


2)在 object 类中有 wait() 和 notifyAll() 方法,可以对线程进行等待和唤醒的操作

代码实现:

打印类Printer:

/**
 * Printer类里面提供三个方法,ABC,分别打印ABC这三个字符串
 * @author zhmm
 *
 */
public class Printer {
	//创建一个标记数,标记数为1,2,3时分别表示运行该线程
	private int flag = 1;
	//使用synchronized隐式锁来锁住当前对象,当当前线程运行A方法时,该对象的BC方法堵塞,处于等待状态
	//使用wait()和notifyAll()是当前线程等待和唤醒其它线程
	public void A() throws InterruptedException {
		synchronized (this) {
			while(flag != 1) {
				this.wait();
			}
			System.out.println("A");
			flag = 2;
			this.notifyAll();
		}
	}
	public void B() throws InterruptedException {
		synchronized (this) {
			while(flag != 2) {
				this.wait();
			}
			System.out.println("B");
			flag = 3;
			this.notifyAll();
		}
	}
	public void C() throws InterruptedException {
		synchronized (this) {
			while(flag != 3) {
				this.wait();
			}
			System.out.println("C");
			flag = 1;
			this.notifyAll();
		}
	}
}

测试类ABCShunXuTest:

public class ABCShunXuTest {
	public static void main(String[] args) {
		final Printer p = new Printer();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.A();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.B();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.C();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
}

测试结果:


扩展:也可以使用显示锁Lock来来完成,只不过加锁和解锁需要自己手动完成:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 使用显式锁来进行对当前对象进行加锁
 * @author zhmm
 *
 */


public class Printer1 {
	//添加互斥锁
	private ReentrantLock r = new ReentrantLock();
	private Condition c1 = r.newCondition();
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();
	private int flag = 1;
	//使用Lock显式锁来锁住当前对象,当当前线程运行A方法时,该对象的BC方法堵塞,处于等待状态
	//使用await()和signal()是当前线程等待和唤醒其它线程
	//要注意的是使用显式锁需要自己进行加锁和解锁操作
	public void A() throws InterruptedException {
		r.lock();
		while(flag != 1) {
			c1.await();
		}
		System.out.println("A");
		flag = 2;
		c2.signal();
		r.unlock();
	}
	public void B() throws InterruptedException {
		r.lock();
		while(flag != 2) {
			c2.await();
		}
		System.out.println("B");
		flag = 3;
		c3.signal();
		r.unlock();
	}
	public void C() throws InterruptedException {
		r.lock();
		while(flag != 3) {
			c3.await();
		}
		System.out.println("C");
		flag = 1;
		c1.signal();
		r.unlock();
	}
}
  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值