此为学习笔记。
实现:当传入方法的key值相同时,需要相隔一秒打印,不相同的,第一时间同时打印。
问题:用synchronized(key)锁住打印的代码,key中有两条数据都是"1",预期效果应该是可以相隔一秒打印,但结果却是同时的。先上代码。
public class ThreadTest3 extends Thread{
private String key;
private String value;
private TestDo testDo;
public ThreadTest3(String key ,String key2 , String value) {
this.key=key+key2;
this.value = value;
this.testDo = TestDo.getInstance();
}
@Override
public void run() {
super.run();
testDo.dome(key, value);
}
public static void main(String[] args) {
ThreadTest3 test1 = new ThreadTest3("1","","1");
ThreadTest3 test2 = new ThreadTest3("2","","2");
ThreadTest3 test3 = new ThreadTest3("1","","3");
ThreadTest3 test4 = new ThreadTest3("3","","4");
test1.start();
test2.start();
test3.start();
test4.start();
}
}
class TestDo{
private TestDo() {}
private static TestDo instance = new TestDo();
public static TestDo getInstance() {
return instance;
}
public void dome(String key , String value) {
try {
synchronized(key) {
Thread.sleep(1000);
System.out.println(key+":"+value+"-"+System.currentTimeMillis()/1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果
如图所示,同一秒打印。
问题原因:常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的当时产生新的对象,以实现内容没有改变,任然相等(都还以为"1"),但对象却不再是同一个的效果。
验证:当我们将类构造方法中this.key=key+key2;修改为this.key=key;,这个时候输出正确。
由于学习内容是线程,所以上面的验证为的是证明问题原因的正确性。现在我们用线程的方法解决。(以下方法均值修改内部类)
方法一:
解决办法:用List记录每次传入的key是否已存在于list中,若不存在则add(),若存在,则找出之前传入synchronized中的key,保证是同一个对象传入synchronized,达到解决问题的目的。
class TestDo{
private List<String> keys = new ArrayList<String>();
private TestDo() {}
private static TestDo instance = new TestDo();
public static TestDo getInstance() {
return instance;
}
public void dome(String key , String value) {
String str = key;
//如果list中不存在此key,则放入list
if(!keys.contains(str)) {
keys.add(str);
}else {//若已存在key,则找出之前传入synchronized中的key,保证是同一个对象,达到解决问题的目的
for(int i = 0 ; i < keys.size(); i++) {
if(key.equals(keys.get(i))) {
str = keys.get(i);
}
}
}
try {
synchronized(str) {
Thread.sleep(1000);//相同的key,打印时间相隔一秒
System.out.println(key+":"+value+"-"+System.currentTimeMillis()/1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
方法二:
解决办法:将for循环换成迭代器,如图
存在问题:遍历时,操作集合会报错,上图。
解决办法:list的实现换成CopyOnWriteArrayList,此时允许遍历集合时,操作集合。
以上,为学习笔记,勿喷。