`
ftj20003
  • 浏览: 130593 次
  • 性别: Icon_minigender_1
  • 来自: ...
社区版块
存档分类
最新评论

一道多线程趣味热身题

    博客分类:
  • Java
阅读更多
    保持对知识点或者技术的熟悉度对于程序员至关重要,要学会一个技术点可能不需要很多精力或者时间,但是要精通或者长时间保持对其的熟悉程度往往非常困难。个人的观点是通过演练对自己掌握的知识点保鲜是一个很好的途径。这其中包括了写Blog阐述,对周围的爱好者演讲,与其他程序员交流辩论以及最重要的多找机会去写相关的程序或者解决相关的问题,难点来加深理解和延长熟悉度。所以我通过写Blog和找相关的问题解决来试图延长对某个知识点的熟知度,毕竟长时间不用,不理会的东西不管什么都会自然的被遗忘。多线程相关的编程对于Web应用开发过程中使用的机会很少,所以只有自己找一些题目或者问题演练。

    之前看到过这样的一个题目:三个线程,线程名分别为A、B、C,设计程序使得三个线程循环打印“ABC”10次后终止。这个题目相对于我之前写的那个疑似Google面试题来说要简单的多。可以看成是三个线程写一个文件的简化版本,不需要考虑调整优先级。主要思路一致:记录最后一次写入的位置。具体的代码示例如下:
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: yanxuxin
 * @date: 2010-2-25
 */
public class WarmUpForThread {

	/** 线程名数组*/
	private final String[] names = {"A","B","C"};
	
	/** 循环执行的总长度*/
	private final int size = names.length * 10;
	
	private final ReentrantLock lock = new ReentrantLock();
	
	/** 记录最后一次写入的元素在数组的位置*/
	private int latestPos = 0;
	
	/** 记录总体打印次数*/
	private volatile int counter = 1;
	
	public static void main(String[] args) {
		WarmUpForThread demo = new WarmUpForThread();
		demo.startDemo();
	}
	
	/**
	 * 开启3个线程,线程名对于数组常量
	 */
	public void startDemo() {
		for(String name : names) {
			new PrintWorker(name).start();
		}
	}
	
	/**
	 * 多线程竞争执行具体打印任务的方法
	 * @throws InterruptedException
	 */
	public void print() throws InterruptedException {
		String name = Thread.currentThread().getName();
		
		lock.lockInterruptibly(); // 可以响应线程中断信号
		try{
			if(names[latestPos].equals(name) && counter <= size) {
				latestPos = (latestPos + 1) % names.length;
				counter++;
				System.out.print(name);
			}
		}
		finally{
			lock.unlock();
		}
		
		Thread.sleep(50);
	}
	
	/**
	 * 执行打印任务的线程
	 * @author: yanxuxin
	 * @date: 2010-2-25
	 */
	class PrintWorker extends Thread {
		
		public PrintWorker(String name) {
			super(name);
		}
		
		public void run() {
			while(counter <= size) {
				try {
					print();
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

    这里用于记录以操作总数的counter利用了volatile的可见性的特性。多个子线程可以同时得到最新的counter的值,又由于对counter的递增与改变latestPos值的操作是一个共同的原子操作,所以可以使用lock来确保counter++的线程安全。故基于上述的原因使用volatile关键字。另外一点是在print方法里也使用counter <= size检查条件的原因是:当counter=29,一个线程执行counter++之前,其余的线程中的一个或者多个counter <= size条件依然满足,故都进入竞争等待阶段,所以一旦进入lock块就意味着counter实际已经30了,不应该再继续打印,故print()方法内依然有此条件检查。

    这个例子虽然相对简单,但是也是一样考察了线程调度的问题。暂时没有无锁实现的清晰的思路,所以只是使用另一种线程同步的机制ReentrantLock演练一下。最近开始找工作,不知笔试或者面试中能不能激发多一点的多线程方面的灵感和领悟,呵呵。找工作虽然比较难,但是希望自己能在这个陌生的城市坚持并如愿...
4
0
分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

Global site tag (gtag.js) - Google Analytics