【Java多线程与并发库】20.java线程面试题3

题目描述:
现有程序同时启动了4个线程去调用TestDo.doSome(key,value)方法,由于TestDo.doSome(key,value)
方法内的代码是先暂停1秒,然后再输出以秒为单位的当前时间值,所以,会打印出4个相同的时间值,
如下所示:
4:4:1258444892
1:1:1258444892
3:3:1258444892
1:2:1258444892
请修改代码,如果有几个线程调用TestDo.doSome(key,value)方法时,传递进去的key相等(equals比较为
true),则这几个线程应互斥排队输出结果,即当有两个线程的key都是“1”时,它们中的一个要比另外
其它线程晚1秒输出结果。如下所示:
4:4:1258444892
1:1:1258444892
3:3:1258444892
1:2:1258444896
总之,当每个线程中指定的key相等时,这些相等key的线程应每隔一秒依次输出时间(要用互斥),如果
key不同,则并行执行(相互之间不互斥)。原始代码如下:
package cn.edu.hpu.test;

//不能改动此ThreadTest9类
public class ThreadTest9 extends Thread{
	
	private TestDo testDo;
	private String key;
	private String value;
	
	public ThreadTest9(String key,String key2,String value){
		this.testDo=TestDo.getInstance();
		/*
		 * 常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的
		 * 对象,以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果
		 * */
		this.key=key+key2;
		this.value=value;
	}
	
	public static void main(String[] args) {
		ThreadTest9 a=new ThreadTest9("1","","1");
		ThreadTest9 b=new ThreadTest9("1","","2");
		ThreadTest9 c=new ThreadTest9("3","","3");
		ThreadTest9 d=new ThreadTest9("4","","4");
		System.out.println("begin:"+(System.currentTimeMillis()/1000));
		a.start();
		b.start();
		c.start();
		d.start();
	}
	
	public void run(){
		testDo.doSome(key,value);
	}
}


class TestDo{
	private TestDo(){}
	private static TestDo _instance = new TestDo();
	public static TestDo getInstance(){
		return _instance;
	}
	
	public void doSome(String key, String value) {
		//以下大括号内的是需要局部同步的代码,不能改动!
		{
			try {
				Thread.sleep(1000);
				System.out.println(key+":"+value+":"
						+(System.currentTimeMillis()/1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

分析:需要实现“当两个线程的key都是“1”时,它们中的一个要比另外
其它线程晚1秒输出结果”,那就需要让key一样的两个线程互斥,这里
我们要注意到,ThreadTest9中的testDo拿的都是TestDo的静态内部成员变量,
所以每一个ThreadTest9线程类都使用的是同一个TestDo对象的doSome()方法,
所以可以在TestDo对象的doSome()方法以及周围做些判定。

想法是,开一个List,每次有新的线程执行doSome()方法的时候,首先判断List中
有没有这个key对象,如果没有可以添加该对象进List,如果有,看一下List中有
哪个内容与这个key相等的对象,然后把现在的key转化成List中的key对象。

然后以这个key对象为互斥锁的对象,将doSome()方法包裹在互斥锁中。

以上的所有操作是为了使内容一样的对象,使用同一个互斥锁来进行互斥操作,具体代码:
package cn.edu.hpu.test;

import java.util.ArrayList;
import java.util.Iterator;

//不能改动此ThreadTest9类
public class ThreadTest9 extends Thread{
	
	private TestDo testDo;
	private String key;
	private String value;
	
	public ThreadTest9(String key,String key2,String value){
		this.testDo=TestDo.getInstance();
		/*
		 * 常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的
		 * 对象,以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果
		 * */
		this.key=key+key2;
		this.value=value;
	}
	
	public static void main(String[] args) {
		ThreadTest9 a=new ThreadTest9("1","","1");
		ThreadTest9 b=new ThreadTest9("1","","2");
		ThreadTest9 c=new ThreadTest9("3","","3");
		ThreadTest9 d=new ThreadTest9("4","","4");
		System.out.println("begin:"+(System.currentTimeMillis()/1000));
		a.start();
		b.start();
		c.start();
		d.start();
	}
	
	public void run(){
		testDo.doSome(key,value);
	}
}


class TestDo{
	private TestDo(){}
	private static TestDo _instance = new TestDo();
	public static TestDo getInstance(){
		return _instance;
	}
	
	private ArrayList keys=new ArrayList();
	public void doSome(String key, String value) {
		Object o=key;
		if(!keys.contains(o)){
			keys.add(o);
		}else{
			for(Iterator iter=keys.iterator();iter.hasNext();){
				Object oo=iter.next();
				if(oo.equals(o)){
					o=oo;
				}
			}
		}
		
		synchronized (o) {
			//以下大括号内的是需要局部同步的代码,不能改动!
			{
				try {
					Thread.sleep(1000);
					System.out.println(key+":"+value+":"
							+(System.currentTimeMillis()/1000));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}


效果:
【Java多线程与并发库】20.java线程面试题3_第1张图片


但是多运行几次就会出现这种结果:
【Java多线程与并发库】20.java线程面试题3_第2张图片

原因是我们使用的List是普通的List,是线程不安全的,这个时候在遍历List的时候,
很可能其它线程打断该方法,去操作了List,然后导致原来的List遍历出现异常或许
发生死循环,所以,我们应该使用线程安全的List集合,即同步集合。

我们这里使用CopyOnWriteArrayList读写同步List,来解决线程不安全的问题。
private CopyOnWriteArrayList keys=new CopyOnWriteArrayList();
public void doSome(String key, String value) {
	Object o=key;
	if(!keys.contains(o)){
		keys.add(o);
	}else{
		for(Iterator iter=keys.iterator();iter.hasNext();){
			Object oo=iter.next();
			if(oo.equals(o)){
				o=oo;
			}
		}
	}
	
	synchronized (o) {
		//以下大括号内的是需要局部同步的代码,不能改动!
		{
			try {
				Thread.sleep(1000);
				System.out.println(key+":"+value+":"
						+(System.currentTimeMillis()/1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

结果:
反复运行多次也不会出现同步的异常了。
转载请注明出处:http://blog.csdn.net/acmman/article/details/53142264

你可能感兴趣的:(Java线程与并发,Java多线程与并发库)