volatile并不能真正的保证线程安全,只能确保一个线程修改了数据后,其他线程能够看到这个改动,但两个线程同时修改某个数据时,依然会冲突。
public class AccountVol implements Runnable {
static AccountVol instance = new AccountVol();
static volatile Integer i = 0;
public static void increase(){
i ++;
}
public void run() {
synchronized (i) {
for (int i = 0; i < 1000000; i++) {
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);
}
}
上面的最终i的值应该为2000000,但事实是结果往往总小于该值。由于两个线程同时对i进行写入,其中一个线程会覆盖另外一个线程。
为了能从根本上解决这个问题,可以通过synchronized实现线程间的同步,对同步的代码加锁,使得每次只有一个线程进入同步块,保证线程的安全。
synchronized的几种用法如下:
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁;
- 直接作用于实例方法:相当于对当前实例加锁;
- 直接作用于静态方法:相当于对当前类加锁,进入同步代码块前要获得当前类的锁;
将上面的代码块稍作修改,即可实现想要的结果,每次只有一个线程进行i++操作:
public void run() {
for (int j = 0; j < 1000000; j++) {
synchronized (instance){
i++;
}
}
}
synchronized的四种用法
1 修饰方法
下面两种方法等价的,都锁定了整个方法时的内容。
//方法一:修饰一个方法
public synchronized void method()
{
// todo
}
//方法二:修饰一个代码块
public void