从一份代码讲起
首先看一份多线程代码,这份代码的执行结果是什么呢?
class Scratch {
public static void main(String[] args) {
Thing thing = new Thing();
Thing thing2 = new Thing();
Thread thread1 = new Thread(thing);
Thread thread2 = new Thread(thing2);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thing.num);
}
static class Thing implements Runnable {
private static int num = 0;
@Override
public synchronized void run() {
for (int i = 0; i < 10000; i++) {
num++;
}
}
}
}
20000吗?并不是。正确答案是未知。。。明明貌似已经使用了synchronized关键字,为什么还会造成并发的问题呢?这就牵扯到了一个对象锁和表锁的问题。
对象锁和表锁的区别
java在对实例方法加锁的时候默认采用的是对象锁,所谓对象锁就是每个对象持有一把锁,多线程共同争用这个锁。但是上述的例子,我们声明了两个对象,导致拥有了两把锁,因此结果就会不符合预期的情况。
如何解决
解决这个问题,只需要使用类锁即可。下面给出了两种实现方式
第一种实现方式为使用synchronized 加类锁
static class Thing implements Runnable {
private static int num = 0;
@Override
public void run() {
synchronized (Thread.class) {
for (int i = 0; i < 10000; i++) {
num++;
}
}
}
}
第二种方式是使用synchronized修饰的静态方法
static class Thing implements Runnable {
private static int num = 0;
@Override
public void run() {
add();
}
public synchronized static void add(){
for (int i = 0; i < 10000; i++) {
num++;
}
}
}
经验证,上述两种方法均可以正确实现预期目标。