线程安全是并行程序的根基;
volatile关键字只能保证一个线程修改数据后,其他线程可见这个修改;但两个或多个线程同时修改某一个数据时依然冲突;
以下例子最终结果小于20000000,同一时刻2个线程读取i的值后分别执行i++操作,虽然执行了2次i++操作,但i的值只增加了1次;
public class VolatileErrorDemo implements Runnable {
private static VolatileErrorDemo instance = new VolatileErrorDemo();
private static volatile int i = 0;
public static void increase() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
synchronized关键字的作用是实现线程间的同步;用法如下:
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得指定对象的锁;
- 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁;
- 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁;
将synchronized关键字作用于给定对象实例instance,保证只有一个线程执行i++操作:
在main方法中创建线程时引用的是同一个instance实例,所以对于increase()方法加上synchronized关键字对当前实例加锁,实现线程同步;
public class SynchronizedDemo implements Runnable {
private static SynchronizedDemo instance = new SynchronizedDemo();
private static int i = 0;
public synchronized void increase() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
或如下形式,等价代码如下:
public class SynchronizedDemo2 implements Runnable {
private static SynchronizedDemo2 instance = new SynchronizedDemo2();
private static int i = 0;
@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
synchronized(instance) {
i++;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
如果不引用同一个Runnable实例,关注的是自己的对象锁,不能保证线程安全,需要在increase()方法上再增加static关键字,即使指向不同的Runnable实例,但静态方法是对于当前类加锁,而非当前实例,因此可以保证正确同步:
public class SynchronizedStaticDemo implements Runnable {
private static int i = 0;
public synchronized static void increase() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
increase();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new SynchronizedStaticDemo());
Thread t2 = new Thread(new SynchronizedStaticDemo());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}