参考自《实战Java高并发程序设计》
synchronized的三种用法:
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁
- 直接作用于实例方法:相当于给当前实例加锁
- 直接作用于静态方法:相当于给当前类加锁
给对象加锁
下面这段代码会执行i++操作10000000次,由于创建了两个线程,我们期望打印的结果是20000000
public class AccountingSync implements Runnable{
static AccountingSync instance=new AccountingSync();
static int i=0;
@Override
public void run() {
for(int j=0;j<10000000;j++){
//给instance对象加锁
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();//join:等待当前执行的线程结束,结束后马上执行该线程
t2.join();
System.out.println(i);
}
}
每当线程进入被synchronized关键字修饰的代码段,都会请求instance实例的锁。如果当前有其它线程正持有这把锁,那么新来的线程就必须等待。这样就保证了每次只有一个线程执行i++操作
给实例方法加锁
上述代码也可以这样写:
public synchronized void increase(){
i++;
}
@Override
public void run() {
for(int j=0;j<10000000;j++){
// synchronized (instance){
// i++;
// }
increase();
}
}
最终运行结果是和第一个例子一样的,这就是直接给实例方法加锁
给类加锁
如果上述代码的main方法中这样写:
Thread t1=new Thread(new AccountingSync);
Thread t2=new Thread(new AccountingSync);
这种同步方式是错误的!
前文的代码加的是对象锁,两个线程指向的两个对象,换言之,这两个线程使用的两把不同的锁
但只要把对象锁改成类的锁就行了
public static synchronized void increase(){
i++;
}
这样就可以正确同步了