如何实现三个线程轮流打印5个数,一直打印到75为止。

一、具体问题:
启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20….以此类推, 直到打印到75。
二、解决方法:
法1:用Synchronized

package anheng;

public class NewThreadSynchronized {
	public static void main(String[] args) {
		// 创建PrintNum对象
		PrintNumSynchronized printNum = new PrintNumSynchronized();
		// 创建线程1
		new Thread(new Runnable() {

			@Override
			public void run() {
				while (printNum.num <= 65) {
					printNum.fun1();
				}
			}
		},"线程1").start();

		// 创建线程2
		new Thread(new Runnable() {

			@Override
			public void run() {
				while (printNum.num <= 70) {
					printNum.fun2();
				}
			}
		},"线程2").start();

		// 创建线程3
		new Thread(new Runnable() {

			@Override
			public void run() {
				while (printNum.num <= 75) {
					printNum.fun3();
				}
			}
		},"线程3").start();
	}
}

class PrintNumSynchronized {
	// 从1开始打印
	public int num = 1;
	// 初始标识为1,代表线程1,后面的2和3代表线程2和线程3
	public int PrintId = 1;

	// synchronized修饰标识方法1
	public synchronized void fun1() {
		while (PrintId != 1) {
			try {
				wait();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// 打印5个数
		print();
		// PrintId切换为2
		PrintId = 2;
		notifyAll();
	}

	// synchronized修饰标识方法2
	public synchronized void fun2() {
		while (PrintId != 2) {
			try {
				wait();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// 打印5个数
		print();
		// PrintId切换为3
		PrintId = 3;
		notifyAll();
	}

	// synchronized修饰标识方法3
	public synchronized void fun3() {
		while (PrintId != 3) {
			try {
				wait();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// 打印5个数
		print();
		// PrintId切换为1
		PrintId = 1;
		notifyAll();
	}

	private void print() {
		// 依次打印5个数
		System.out.println(Thread.currentThread().getName() + ":" );
		for (int i = 0; i < 5; i++,num++) {
			System.out.println(num);
		}
	}
}

法2:用ReentrantLock

package anheng;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class NewThreadLock {
	public static void main(String[] args) {
		PrintNumLock printNume = new PrintNumLock();
		new Thread(new Runnable() {

			@Override
			public void run() {
				while (printNume.getNum() <= 65) {
					printNume.lock1();
				}
			}
		}, "线程1").start();

		new Thread(new Runnable() {

			@Override
			public void run() {
				while (printNume.getNum() <= 70) {
					printNume.lock2();
				}
			}
		}, "线程2").start();

		new Thread(new Runnable() {

			@Override
			public void run() {
				while (printNume.getNum() <= 75) {
					printNume.lock3();
				}
			}
		}, "线程3").start();
	}

}

class PrintNumLock {
	// 数字从1开始
	private int num = 1;
	// 初始标识为1,代表线程1,后面的2和3代表线程2和线程3
	private int PrintId = 1;

	// 创建num的get和set方法来设置和获取参数
	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	// 创建lock
	final ReentrantLock lock = new ReentrantLock();
	// 创建3个条件对象
	final Condition condition1 = lock.newCondition();
	final Condition condition2 = lock.newCondition();
	final Condition condition3 = lock.newCondition();

	// lock1
	public void lock1() {
		lock.lock();
		try {
			while (PrintId != 1)
				try {
					condition1.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			// 如果是线程1,则打印5个数
			print();
			// 打印完成后,把标识置为2
			PrintId = 2;
			// 唤醒lock2
			condition2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	// lock2
	public void lock2() {
		lock.lock();
		try {
			while (PrintId != 2)
				try {
					condition2.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			// 如果是线程2,则打印5个数
			print();
			// 打印完成后,把标识置为3
			PrintId = 3;
			// 唤醒lock3
			condition3.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	// lock3
	public void lock3() {
		lock.lock();
		try {
			while (PrintId != 3)
				try {
					condition3.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			// 如果是线程3,则打印5个数
			print();
			// 打印完成后,把标识置为1
			PrintId = 1;
			// 唤醒lock1
			condition1.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	private void print() {
		System.out.println(Thread.currentThread().getName() + ":" );
		for (int i = 0; i < 5; i++,num++) {
			System.out.println(num);
		}
	}

}

法3:把3个线程各自的方法合成一个,然后还是用Synchronized:

package anheng;

public class NewThread {
	public static void main(String[] args) {
		Object o = new Object();

		new Thread(new PrintRunnable(o, 1), "线程1").start();
		new Thread(new PrintRunnable(o, 2), "线程2").start();
		new Thread(new PrintRunnable(o, 3), "线程3").start();

	}
}

class PrintRunnable implements Runnable {
	private final Object o;
	private int threadId;
	private static volatile int num = 1;

	public PrintRunnable(Object o, int threadId) {
		this.o = o;
		this.threadId = threadId;
	}

	@Override
	public void run() {
         // 取前面三个方法条件的交集
		while (num <= 65) {
			synchronized (o) {
				while (num / 5 % 3 + 1 != threadId) {
					try {
						o.wait();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				System.out.println(Thread.currentThread().getName() + ":");
				for (int i = 0; i < 5; i++, num++) {
					System.out.println(num);
				}
				o.notifyAll();
			}
		}

	}

}

三种方法的输出结果都是一样的,如下所示:
在这里插入图片描述
三种方法的对比:
(1)比较同样是synchronized修饰的法1和法3:
A、法1和法3都是用synchronized实现,但不同的是法1修饰方法,法2修饰代码块;
B、法3是把法1中的三个方法合在一起,判断条件是三个方法的条件交集num<=65和num/5%3+1!=threadId,减少了大量冗余代码。
(2)比较synchronized修饰的法1和ReentrantLock实现的法2:
法1的中三个方法的判断条件都是!=表示当前方法的线程Id,那满足条件的是另外两个线程,让两个线程同时wait也一起notifyAll则会让大家再次竞争锁,不能保证线程第一时间获得自己的方法,从而效率比较低;而法2中的三个方法条件虽然也是一样的,但唤醒的线程是指定好的,不会同时唤醒两个,所以更精确。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值