Java Synchronized 塊
Synchronized塊是用來解決race condition。Synchronized塊是基于對(duì)象,作用在同一對(duì)象的同步快保證在同一時(shí)間只有一個(gè)線程處理。Synchronized關(guān)鍵字可以作用于一下四種情況:
- 實(shí)例方法
- 靜態(tài)方法
- 實(shí)例方法內(nèi)的代碼塊
- 靜態(tài)方法內(nèi)的代碼塊
Synchronized Instance Methods
public synchronized void add(int value){
this.count += value;
}
實(shí)例方法加synchronized是同步的其擁有該方法的對(duì)象。如果有多個(gè)實(shí)例對(duì)象,那么每個(gè)對(duì)象在同一時(shí)間都只能有一個(gè)線程執(zhí)行同步方法。
Synchronized Static Methods
public static synchronized void add(int value){
this.count += value;
}
靜態(tài)方法加synchronized是同步其擁有該方法的類。由于一個(gè)JVM里面只有一個(gè)類,所以在同一個(gè)類中只能有一個(gè)線程執(zhí)行同步方法。
Synchronized Blocks in Instance Methods
public void add(int value){
synchronized(this){
this.count += value;
}
}
實(shí)例方法內(nèi)的代碼塊通過指定加鎖對(duì)象(monitor object)來進(jìn)行同步。有時(shí)候不需要對(duì)整個(gè)方法加鎖,只需要對(duì)方法內(nèi)的一部分加鎖,可以使用該方法。
Synchronized Blocks in Static Methods
public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
靜態(tài)方法內(nèi)的代碼塊同步的是擁有該方法的類。在上述例子中,一個(gè)線程在同一時(shí)刻只能執(zhí)行一個(gè)方法。如果log2同步快內(nèi)加鎖的是不同的類,那么一個(gè)線程可以同時(shí)執(zhí)行這兩個(gè)方法。
Java Volatile關(guān)鍵字
volatile關(guān)鍵字保證變量的可見性

多線程應(yīng)用在處理非volatile變量的時(shí)候,先把變量的值拷貝一份放到cpu cache里面,以此提高效率。
volatile變量保證每次從main memory里面讀取變量最新的值,每次修改以后將變量的值更新到main memory里面。Java5以后的volatile關(guān)鍵字提供了Happens Before Guarantee
- 如果線程A寫了一個(gè)volatile變量,接著線程B讀取了同一個(gè)volatile變量。那么線程A更新volatile變量之前的所有變量,在線程B讀取volatile變量之后,都是可見的。
- 讀寫volatile變量的操作不能被JVM重新排序,但是volatile變量之前和之后的操作可以被重新排序。
public class Exchanger {
private Object object = null;
private volatile hasNewObject = false;
public void put(Object newObject) {
while(hasNewObject) {
//wait - do not overwrite existing new object
}
object = newObject;
hasNewObject = true; //volatile write
}
public Object take(){
while(!hasNewObject){ //volatile read
//wait - don't take old object (or null)
}
Object obj = object;
hasNewObject = false; //volatile write
return obj;
}
}
可以利用這個(gè)特性,不用每個(gè)變量都加volatile。上面的例子中,如果只有一個(gè)線程執(zhí)行put(), 另外一個(gè)線程執(zhí)行take(),那么不需要同步塊也能保證線程安全。
sharedObject.nonVolatile1 = 123;
sharedObject.nonVolatile2 = 456;
sharedObject.nonVolatile3 = 789;
sharedObject.volatile = true; //a volatile variable
int someValue1 = sharedObject.nonVolatile4;
int someValue2 = sharedObject.nonVolatile5;
int someValue3 = sharedObject.nonVolatile6;
volatile保證了sharreObject.volatile的順序不被重排,但是前3個(gè)變量和后面3個(gè)變量的執(zhí)行順序可能被JVM重排。
僅僅volatile是不夠的
只要一個(gè)線程需要先讀一個(gè)volatile變量,然后在需要在這個(gè)變量值的基礎(chǔ)之上生成一個(gè)新的值,那么volatile變量不再試線程安全的。
當(dāng)有多個(gè)線程對(duì)volatile變量進(jìn)行寫操作的時(shí)候,就有可能造成race condition。

什么時(shí)候volatile變量是可靠的
當(dāng)一個(gè)線程進(jìn)行volatile變量的讀寫,另外一個(gè)變量只進(jìn)行volatile變量的讀的時(shí)候,變量是線程安全的額。
volatile變量的性能
由于volatile變量需要從main memory里面讀取數(shù)據(jù),多以性能比直接從cpu 緩存中讀取數(shù)據(jù)要高。多以在必要的時(shí)候再使用volatile關(guān)鍵字。
ThreadLocal
ThreadLocal類允許生成一個(gè)只能被一個(gè)線程進(jìn)行讀寫的變量。即使兩個(gè)線程執(zhí)行同一段代碼,兩個(gè)線程也不能看到對(duì)方的ThreadLocal變量
public class ThreadLocalExample {
public static class MyRunnable implements Runnable {
private ThreadLocal<Integer> threadLocal =
new ThreadLocal<Integer>();
@Override
public void run() {
threadLocal.set( (int) (Math.random() * 100D) );
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
thread1.join(); //wait for thread 1 to terminate
thread2.join(); //wait for thread 2 to terminate
}
}
在上面的例子中,雖然是同一個(gè)ThreadLocal成員變量,但是兩個(gè)的值取出來是不相同的。