这两天在学习多线程时遇到了一个奇怪的问题,贴上来供大家参考参考。
首先是一个简单的计数器。
package section_3;
public class Counter extends Thread{
static int count = 0;
synchronized public void incement(){
count++;
}
@Override
public void run(){
for(int i=0; i<10000; i++){
this.incement();
}
}
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Counter t1 = new Counter();
Counter t2 = new Counter();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
您可能会期望该程序最后会打印出20000,但是结果是非常让人失望的,synchronized似乎没有起到相应的作用,结果是不确定的。在我的机子上跑出来大概只有60%的概率会得出正确结果。这种同步失败的原因我还在研究。
下面来看一段正确的计数器代码:
package section_3;
public class AnotherCounter {
static class Counter{
int value = 0;
synchronized int getValue(){
return value;
}
synchronized void increment(){
value++;
}
}
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
final Counter counter = new Counter();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<10000; i++){
counter.increment();
}
}}, "t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0; i<10000; i++){
counter.increment();
}
}}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.getValue());
}
}
其不同之处主要在于取消了一个static变量,下面说说我的看法。
我们在使用synchronized关键字时,只能对对象(Object)进行锁定,那么在这里,首先,count是一个基本类型,不是一个Object,其次,它是一个static变量,是独立于具体实例对象的,即我们无法使用synchronized(this)来通过锁定实例对象来锁定它,因为它不是实例对象的成员变量。所以,解决办法是加入一个锁对象,通过它来对count进行加锁解锁,如下所示。
Object lock = new Object();
synchronized(lock){
//do something about count
}
这样就可以对static 基本类型进行同步了。