synchronized 锁
synchronized关键字最主要有以下3种应用方式:
-
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
-
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
-
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
1 实例方法加锁 (非静态方法)
使用synchronized修饰,以便保证线程安全:加了synchronized 后同时只能有一个线程访问该方法。
但如果多个实例对象操作,非静态方法不能保证线程安全。
public class AccountingSync implements Runnable{//共享资源(临界资源)static int i=0;
?/*** synchronized 修饰实例方法*/public synchronized void increase(){i++;}@Overridepublic void run() {for(int j=0;j<1000000;j++){increase();}}public static void main(String[] args) throws InterruptedException {AccountingSync instance=new AccountingSync();Thread t1=new Thread(instance);Thread t2=new Thread(instance);t1.start();t2.start();t1.join();t2.join();System.out.println(i);}/*** 输出结果:* 2000000*/
}
2.静态方法加锁
当synchronized作用于静态方法时,其锁就是当前类的class对象锁。当多个不同对象实例同时访问时也能保证线程安全(无论实例多少个对象,静态方法是属于类本身的,只能同时被一个对象访问)。
由于静态成员不专属于任何一个实例对象,是类成员。给静态方法加锁可以保证线程安全。
3.synchronized锁定代码块
public class AccountingSync implements Runnable{static AccountingSync instance=new AccountingSync();static int i=0;@Overridepublic void run() {//省略其他耗时操作....//使用同步代码块对变量i进行同步操作,锁对象为instancesynchronized(instance){for(int j=0;j<1000000;j++){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);}
}
将synchronized作用于一个给定的实例对象instance,即当前实例对象就是锁对象,每次当线程进入synchronized包裹的代码块时就会要求当前线程持有instance实例对象锁,如果当前有其他线程正持有该对象锁,那么新到的线程就必须等待,这样也就保证了每次只有一个线程执行i++;
操作。当然除了instance作为对象外,我们还可以使用this对象(代表当前实例)或者当前类的class对象作为锁,如下代码:
//this,当前实例对象锁
synchronized(this){for(int j=0;j<1000000;j++){i++;}
}
?
//class对象锁
synchronized(AccountingSync.class){for(int j=0;j<1000000;j++){i++;}
}