多線程基礎

格式混亂。。。。可至我的有道雲查看:http://note.youdao.com/noteshare?id=277d8d512f6f80f111bc7a058aa3cb27&sub=743EACECE382418AAEEE56CA086E5CD1

同步(Synchronous)和異步(Asynchronous)

同步和異步通常用來形容一次方法調用。同步方法調用一旦開始,調用者必須等到方法調用返回後,才能繼續後續的行爲。
異步方法調用更像一個消息傳遞,一旦開始,方法調用就會立即返回,調用者就可以繼續後續的操作。
而異步方法通常會在另外一個線程中“真實”地執行。整個過程,不會阻礙調用者的工作。

併發(Concurrency)和並行(Parallelism)
併發偏重於多個任務 交替 執行,而多個任務之間有可能還是串行的。
並行是真正意義上的“同時執行”。

臨界區
臨界區用來表示一種公共資源或者說是共享數據,可以被多個線程使用。但是每一次,只能有一個線程使用它,一旦臨界區資源被佔用,其他線程要想使用這個資源,就必須等待。

阻塞(Blocking )和非阻塞(Non-Blocking )
比如一個線程佔用了臨界區資源,那麼其他所有需要這個資源的線程就必須在這個臨界區中進行等待。等待會導致線程掛起,這種情況就是阻塞。
此時,如果佔用資源的線程一直不願意釋放資源,那麼其他所有阻塞在這個臨界區上的線程都不能工作。
非阻塞的意思與之相反,它強調沒有一個線程可以妨礙其他線程執行。所有的線程都會嘗試不斷前向執行。

死鎖(Deadlock )、飢餓(Starvation )和活鎖(Livelock )
死鎖(Deadlock ):兩個或多個線程相互佔用了其他線程的鎖,並都未釋放。、
飢餓(Starvation ):是指某一個或者多個線程因爲種種原因無法獲得所需要的資源,導致一直無法執行。
比如它的線程優先級可能太低,而高優先級的線程不斷搶佔它需要的資源,導致低優先級線程無法工作。
活鎖(Livelock): 兩個線程都主動的將資源釋放給他人使用,導致資源在兩個線程中跳動,而沒有一個線程可以同時拿到所有資源而正常執行。

併發級別
阻塞、無飢餓、無障礙、無鎖、無等待。
阻塞(Blocking ):一個線程是阻塞的,那麼在其他線程釋放資源之前,當前線程無法繼續執行。當我們使用synchronized 關鍵字,或者重入鎖時,我們得到的就是阻塞的線程。
無飢餓(Starvation-Free ):如果線程之間是有優先級的,那麼線程調度的時候總是會傾向於滿足高優先級的線程。也就是說,對於同一資源的分配是不公平的。對於非公平鎖,系統允許高優先
級的線程插隊,這樣有可能導致低優先級線程產生飢餓。但如果鎖是公平的,就是無飢餓。
無障礙(Obstruction-Free ):無障礙是一種最弱的非阻塞調度。兩個線程如果是無障礙的執行,那麼他們不會因爲臨界區的問題導致一方被掛起。
無鎖(Lock-Free ):鎖的並行都是無障礙的。在無鎖的情況下,所有的線程都能嘗試對臨界區進行訪問,但不同的是,無鎖的併發保證必然有一個線程能夠在有限步內完成操作離開臨界區。
無等待(Wait-Free ):無鎖只要求有一個線程可以在有限步內完成操作,而無等待則在無鎖的基礎上更進一步進行擴展。它要求所有的線程都必須在有限步內完成,這樣就不會引起飢餓問題。如
果限制這個步驟上限,還可以進一步分解爲有界無等待和線程數無關的無等待幾種,它們之間的區別只是對循環次數的限制不同。

多線程的原子性、可見性和有序性
原子性(Atomicity):原子性是指一個操作是不可中斷的。即使是多個線程一起執行的時候,一個操作一旦開始就不會不給其他線程干擾。
可見性(Visibility ):可見性是指當一個線程修改了某一個共享變量的值,其他線程是否能夠立即知道這個修改。
有序性(Ordering ):程序在執行時,可能會進行指令重排,重排後的指令與原指令的順序未必一致。(指令重排不會使串行的語義邏輯發生問題。但無法保證多線程間的語義也一致。)

線程狀態(Thread在State中定義的):
NEW 狀態表示剛剛創建的線程,這種線程還沒開始執行。等到線程的 start() 方法調用時,才表示線程開始執行。
當線程執行時,處於 RUNNABLE 狀態,表示線程所需的一切資源都已經準備好了。如果線程在執行過程中遇到了
synchronized 同步塊,就會進入 BLOCKED 阻塞狀態,這時線程就會暫停執行,直到獲得請求的鎖。 WAITING 和 TIMED_WAITING 都表示等待狀態,它們的區別是 WAITING 會進入一個無時間
限制的等待, TIMED_WAITING 會進行一個有時限的等待。那等待的線程究竟在等什麼呢?一般來說, WAITING 的線程正是在等待一些特殊的事件。比如,通過 wait() 方法等待的線程在等待
notify() 方法,而通過 join() 方法等待的線程則會等待目標線程的終止。一旦等到了期望的事件,線程就會再次執行,進入 RUNNABLE狀態。
當線程執行完畢後,則進入 TERMINATED 狀態,表示結束。

注意: 從 NEW 狀態出發後,線程不能再回到 NEW 狀態,同理,處於 TERMINATED的線程也不能再回到 RUNNABLE 狀態。

線程的基本操作
新建線程
使用 new 關鍵字創建一個線程對象,並且將它 start() 起來即可。
Thread t1=new Thread(){
@Override
public void run(){
System.out.println(“Hello, I am t1”);
}
};
t1.start();

需要執行的任務必須重載run()方法

上述代碼使用匿名內部類,重載了 run() 方法,還可以集成Thread類重載run()方法和實現Runnable接口。

常用操作:
public class CreateThread3 implements Runnable {
public static void main(String[] args) {
Thread t1=new Thread( new CreateThread3() );
t1.start();
}
@Override
public void run() {
System.out.println(“Oh, I am Runnable”);
}
}

終止線程
一般來說,線程執行完畢就會結束,無須手動關閉。
關閉手段:
Thread.stop(); //已廢棄(強行終止線程)會直接終止線程,並且會立即釋放這個線程所持有的鎖。易導致數據錯誤。

可以定義標記變量,指示線程是否需要退出。

線程中斷
線程中斷並不會使線程立即退出,而是給線程發送一個通知,告知目標線程,有人希望你退出啦!至於目標線程接到通知後如何處理,則完全由目標線程自行決定。這點很重要,如果中斷後,
線程立即無條件退出,我們就又會遇到 stop()方法的老問題。

線程中斷有如下三個方法:

public void Thread.interrupt() // 中斷線程(通知目標線程中斷,也就是設置中斷標誌位,中斷標誌位表示當前線程已經被中斷了。)
public boolean Thread.isInterrupted() // 判斷是否被中斷(通過檢查中斷標誌位)
public static boolean Thread.interrupted() // 判斷是否被中斷,並清除當前中斷狀態

@Test
/**
 *雖然對 t1 進行了中斷,但是在 t1 中並沒有中斷處理的邏輯,因此,即使 t1 線程被置上了中斷狀態,但是這個中斷不會發生任何作用。
 */
public void test1() throws Exception {
	Thread t1 = new Thread() {
		@Override
		public void run() {
			while (true) {
				System.out.println("Interruted!");
				Thread.yield();
			}
		}
	};
	t1.start();
	Thread.sleep(2000);
	t1.interrupt();
}


@Test
 /**
 *t1在中斷後處理一下,纔會增加相應的中斷處理代碼
 */
public void test2() throws Exception {

	Thread t1 = new Thread() {
		@Override
		public void run() {
			while (true) {
				if (Thread.currentThread().isInterrupted()) {
					System.out.println("Interruted!");
					break;
				}
				Thread.yield();
			}
		}
	};

	t1.start();
	Thread.sleep(2000);
	t1.interrupt();
}



@Test
 /**
 *當線程在 sleep()休眠時,如果被中斷,會產生InterruptedException異常。
 *Thread.sleep()方法由於中斷而拋出異常,此時,它會清除中斷標記,如果不加處理,那麼在下一次循環開始時,就無法捕獲這個中斷,故在異常處理中,再次設置中斷標記位。
 * 輸出:Interruted When Sleep
 *      Interruted!
 */
public void test3() throws Exception {
	Thread t1 = new Thread() {
		@Override
		public void run() {
			while (true) {
				if (Thread.currentThread().isInterrupted()) {
					System.out.println("Interruted!");
					break;
				}
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					System.out.println("Interruted When Sleep");
					// 設置中斷狀態
					Thread.currentThread().interrupt();
				}
				Thread.yield();
			}
		}
	};
	t1.start();
	Thread.sleep(2000);
	t1.interrupt();
}

等待(wait)和通知(notify)
當在一個對象實例上調用 wait() 方法後,當前線程就會在這個對象上等待。

Object.wait() 方法並不是可以隨便調用的。它必須包含在對應的synchronzied 語句中,無論是 wait() 或者 notify() 都需要首先獲得目標對象的一個監視器。

notify() 喚醒等待的線程,如下圖:

wait() 和 notify() 的工作流程細節,如下圖:

public class SimpleWN {

final static Object object = new Object();

public static class T1 extends Thread {
	public void run() {
		synchronized (object) {
			System.out.println(System.currentTimeMillis() + ":T1 start!");
			try {
				System.out.println(System.currentTimeMillis() + ":T1 wait for object ");
				object.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(System.currentTimeMillis() + ":T1 end!");
		}
	}
}

public static class T2 extends Thread {
	public void run() {
		synchronized (object) {
			System.out.println(System.currentTimeMillis() + ":T2 start!notify one thread");
			object.notify();
			System.out.println(System.currentTimeMillis() + ":T2 end!");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
			}
		}
	}
}

/**
 *輸出:
 *1544081985067:T1 start!
 *1544081985067:T1 wait for object 
 *1544081985067:T2 start!notify one thread
 *1544081985067:T2 end!
 *1544081987067:T1 end!
 */
public static void main(String[] args) {
	Thread t1 = new T1();
	Thread t2 = new T2();
	t1.start();
	t2.start();
}

}

在 T2 通知 T1 繼續執行後, T1 並不能立即繼續執行,而是要等待 T2 釋放 object 的鎖,並重新成功獲得鎖後,才能繼續執行。

注意: Object.wait() 和 Thread.sleep()方法都可以讓線程等待若干時間。除了 wait() 可以被喚醒外,另外一個主要區別就 是
wait() 方法會釋放目標對 象的鎖,而Thread.sleep()方法不會釋 放任何資源。

掛起(suspend )和繼續執行(resume )線程(已廢棄)
suspend()在導致線程暫停的同時,並不會去釋放任何鎖資源。resume()操作, 被掛起的線程才能繼續。

suspend()方法導致線程進入類似死鎖的狀態,如下圖:

public class BadSuspend {

public static Object u = new Object();
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");

public static class ChangeObjectThread extends Thread {
	public ChangeObjectThread(String name) {
		super.setName(name);
	}

	@Override
	public void run() {
		synchronized (u) {
			System.out.println("in " + getName());
			Thread.currentThread().suspend();
		}
	}
}
/** 
 *雖然主函數中已經調用了resume(),但是由於時間先後順序的緣故,那個 resume 並沒有生效!這就導致了線程 t2 被永遠掛起,並且永遠佔用了對象 u 的鎖。
 */
public static void main(String[] args) throws InterruptedException {
	t1.start();
	Thread.sleep(100);
	t2.start();
	t1.resume();
	t2.resume();
	t1.join();
	t2.join();
}

}

可以設置標記變量利用wait和notify 實現suspend和resume功能,如下例:
public class GoodBadSuspend {

public static Object u = new Object();

public static class ChangeObjectThread extends Thread {
	volatile boolean suspendme = false;

	public void suspendMe() {
		suspendme = true;
	}

	public void resumeMe() {
		suspendme = false;
		synchronized (this) {
			notify();
		}
	}
	
	public ChangeObjectThread(String name) {
		super.setName(name);
	}

	@Override
	public void run() {
		while (true) {
			
			synchronized (this) {
				while (suspendme)
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
			}

			synchronized (u) {
				System.out.println(super.getName() + "-----in ChangeObjectThread");
			}
			Thread.yield();
		}
	}
}

public static class ReadObjectThread extends Thread {
	
	public ReadObjectThread(String name) {
		super.setName(name);
	}
	@Override
	public void run() {
		while (true) {
			synchronized (u) {
				System.out.println(super.getName() + "-----in ReadObjectThread");
			}
			Thread.yield();
		}
	}
}

public static void main(String[] args) throws InterruptedException {
	ChangeObjectThread t1 = new ChangeObjectThread("t1");
	ReadObjectThread t2 = new ReadObjectThread("t2");
	t1.start();
	t2.start();
	Thread.sleep(1000);
	t1.suspendMe();
	System.out.println("suspend t1 2 sec");
	Thread.sleep(2000);
	System.out.println("resume t1");
	t1.resumeMe();
}

}

等待線程結束(join )和謙讓(yield)

//表示無限等待,它會一直阻塞當前線程,直到目標線程執行完畢。
public final void join() throws InterruptedException

//給出了一個最大等待時間,如果超過給定時間目標線程還在執行,當前線程也會因爲“等不及了”,而繼續往下執行。
public final synchronized void join(long millis) throws InterruptedException

如下例:
public class JoinMain {
public volatile static int i=0;
public static class AddThread extends Thread{
@Override
public void run() {
for(i=0;i<10000000;i++);
}
}
public static void main(String[] args) throws InterruptedException {
AddThread at=new AddThread();
at.start();
at.join();
System.out.println(i);//10000,若不適用join()可能輸出0或者很小的數字,用join(),則當前線程需等待AddThread執行完畢
}
}

join() 的本質是讓調用線程 wait() 在當前線程對象實例上。
源碼:
public final void join() throws InterruptedException {
join(0);
}

public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }

}

join()方法其實是讓調用線程在當前線程對象上進行等待。當線程執行完成後,被等待的線程會在退出前調用 notifyAll() 通知所有的等待線程繼續執行。
因此, 值得注意的一點是:
不要在應用程序中, 在 Thread 對象實例上使用類似 wait() 或者 notify() 等方法, 因爲這很有可能會影響系統 API 的工作,或者被系統 API 所影響。

Thread.yield():使當前線程讓出CPU。(當前線程在讓出 CPU 後,還會進行 CPU 資源的爭奪)

volatile關鍵字
使用volatile聲明的變量被修改後,應用程序範圍內的所有線程都能夠“看到”這個改動,此外, volatile 也能保證數據的可見性和有序性

public class VolatileTest {

static volatile int i = 0;

public static void main(String[] args) throws Exception {
	Thread[] threads = new Thread[10];
	for (int i = 0; i < 10; i++) {
		threads[i] = new Thread(new PlusTask());
		threads[i].start();
	}
	for (int i = 0; i < 10; i++) {
		threads[i].join();
	}
	System.out.println(i);
}
//輸出跳動  結果不定, 但大概率小於100000, 因爲 i++是線程不安全的操作。
public static class PlusTask implements Runnable {

	@Override
	public void run() {
		for (int k = 0; k < 10000; k++)
			i++;
	}

}

}

線程組

public class ThreadGroupName implements Runnable {

public static void main(String[] args) {
	ThreadGroup tg = new ThreadGroup("PrintGroup");//建立名爲PrintGroup的線程組
        //將T1、T2加入到線程組並命名(線程命名最好有意義)
	Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");
	Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");
	t1.start();
	t2.start();
	System.out.println(tg.activeCount());//獲得活動線程的總數
	tg.list();//打印線程組的中所有的線程信息
       Thread.sleep(5000);
       tg.stop();//停止線程組所有線程(有Thread.stop()一樣的問題))
}

@Override
public void run() {
	String groupAndName = Thread.currentThread().getThreadGroup().getName() + "-"
			+ Thread.currentThread().getName();
	while (true) {
		System.out.println("I am " + groupAndName);
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

}

守護線程(Daemon)
守護線程是一種特殊的線程,它是系統的守護者,在後臺默默地完成一些系統性的服務, 比如垃圾回收線程、 JIT 線程就可以理解爲守護線程。
用戶線程可以認爲是系統的工作線程,如果用戶線程全部結束,這也意味着這個程序實際上無事可做了。守護線程要守護的對象已經不存在了,那麼整個應用程序就自然應該結束。
因此當一個 Java 應用內,只有守護線程時, Java虛擬機就會自然退出。

public class DaemonDemo {

public static class DaemonT extends Thread {
	public void run() {
		while (true) {
			System.out.println("I am alive");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public static void main(String[] args) throws InterruptedException {
	Thread t = new DaemonT();
	t.setDaemon(true);//將線程t設置爲守護線程(必須在線程t  start之前設置,否則主線程會報錯,t則正常運行,但會被當做普通線程)
	t.start();

	Thread.sleep(2000);
}

}

線程優先級

在 Java 中,使用 1 到 10 表示線程優先級,數字越大則優先級越高, 但有效範圍在 1 到 10 之間。一般可以使用內置的三個靜態標量表示:
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

DEMO: 大部分情況下HightPriority 總是比 LowPriority 快(不是所有情況)
public class PriorityDemo {

public static void main(String[] args) throws InterruptedException {
	Thread high = new HightPriority();
	LowPriority low = new LowPriority();
	high.setPriority(Thread.MAX_PRIORITY);
	low.setPriority(Thread.MIN_PRIORITY);
	low.start();
	high.start();
}

public static class HightPriority extends Thread {
	static int count = 0;

	public void run() {
		while (true) {
			synchronized (PriorityDemo.class) {
				count++;
				if (count > 10000000) {
					System.out.println("HightPriority is complete");
					break;
				}
			}
		}
	}
}

public static class LowPriority extends Thread {
	static int count = 0;

	public void run() {
		while (true) {
			synchronized (PriorityDemo.class) {
				count++;
				if (count > 10000000) {
					System.out.println("LowPriority is complete");
					break;
				}
			}
		}
	}
}

}

線程安全與synchronize關鍵字

synchronized可以用於線程同步,保證線程安全,還可以保證線程間的可見性和有序性

如下DEMO,由於i++是線程不安全的操作,所以輸出大概率小於20000000(volatile並不能保證線程安全):
public class AccountingVol implements Runnable {

static AccountingVol instance = new AccountingVol();
static volatile int i = 0;

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 static void increase() {
	i++;
}

@Override
public void run() {
	for (int j = 0; j < 10000000; j++) {
		increase();
	}
}

}

只需如下操作, 即可獲得期望值20000000:
for (int j = 0; j < 10000000; j++) {
synchronized (instance) {
increase();
}
}

synchronize關鍵字用法:
指定加鎖對象:對給定對象加鎖,進入同步代碼前要獲得給定對象的鎖。
直接作用於實例方法:相當於對當前實例加鎖,進入同步代碼前要獲得當前實例的鎖。
直接作用於靜態方法:相當於對當前類加鎖,進入同步代碼前要獲得當前類的鎖。

上述代碼,將 synchronized 作用於一個給定對象 instance,因此,每次當線程進入被synchronized 包裹的代碼段,就都會要求請求 instance 實例的鎖。如果當前有其他線程正持有這把鎖,那
麼新到的線程就必須等待。這樣,就保證了每次只能有一個線程執行 i++ 操作。

//也可以這樣,和上面是等價的
public synchronized void increase() {
i++;
}

錯誤DEMO(t1和t2不是同一個對象,在進入increase()方法時,鎖其實是當前線程new 的對象本身,兩個線程使用的不是同一把鎖,所以線程安全是無法保證的):
public class AccountingSyncBad implements Runnable {

static int i = 0;

public static void main(String[] args) throws InterruptedException {
	Thread t1 = new Thread(new AccountingSyncBad());
	Thread t2 = new Thread(new AccountingSyncBad());
	t1.start();
	t2.start();
	t1.join();
	t2.join();
	System.out.println(i);
}

public synchronized void increase() {
	i++;
}

@Override
public void run() {
	for (int j = 0; j < 10000000; j++) {
		increase();
	}
}

}

但是把increase()方法改爲static就可以了, 因爲如此的話,兩個線程的鎖其實就變成了AccountingSyncBad.class。:
public static synchronized void increase() {
i++;
}

併發下的集合DEMO

併發下ArrayList
錯誤DEMO(可能一:正常結束,輸出2000000(並行程序有問題也不會每次都表現出來);可能二:拋出ArrayIndexOuOfBoundException),1000098(因爲ArrayList在擴容過程中,內
部一致性被破壞,但是由於沒有鎖,訪問到了不一致的內部狀態,導致越界異常;可能三:輸出1793758(於多線程訪問衝突,使得保存容器大小的變量被多線程不正常的訪問,同時兩個
線程也同時對 ArrayList 中的同一個位置進行賦值導致的。)):
public class ArrayListMultiThread {

static ArrayList<Integer> al = new ArrayList<Integer>(10);

public static void main(String[] args) throws InterruptedException {
	Thread t1 = new Thread(new AddThread());
	Thread t2 = new Thread(new AddThread());
	t1.start();
	t2.start();
	t1.join();
	t2.join();
	System.out.println(al.size());
}

public static class AddThread implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 1000000; i++) {
			al.add(i);
		}
	}
}

}

改進:
可以使用線程安全的集合代替;或者:
for (int i = 0; i < 1000000; i++) {
synchronized (AddThread.class) {
al.add(i);
}
}

併發下的HashMap

錯誤DEMO:期望輸出100000,實際:可能一:100000(原因同ArrayList);可能二:94243(原因同ArrayList);可能三:ClassCastException(cannot be cast to
java.util.HashMap$TreeNode);可能四:無法結束()
public class HashMapMultiThread {
static Map<String, String> map = new HashMap<String, String>();

public static void main(String[] args) throws InterruptedException {
	Thread t1 = new Thread(new HashMapMultiThread.AddThread(0));
	Thread t2 = new Thread(new HashMapMultiThread.AddThread(1));
	t1.start();
	t2.start();
	t1.join();
	t2.join();
	System.out.println(map.size());
}

public static class AddThread implements Runnable {
	int start = 0;

	public AddThread(int start) {
		this.start = start;
	}

	@Override
	public void run() {
		for (int i = start; i < 100000; i += 2) {
			map.put(Integer.toString(i), Integer.toBinaryString(i));
		}
	}
}

}

錯誤加鎖DEMO

public class BadLockOnInteger implements Runnable {

public static Integer i = 0;
static BadLockOnInteger instance = new BadLockOnInteger();

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);
}

@Override
public void run() {
	for (int j = 0; j < 10000000; j++) {
		synchronized (i) {
			i++;
		}
	}
}

}

上述DEMO輸出10500781遠小於20000000,因爲Integer在Java裏屬於不變對象。i++實際執行的是
i=Integer.valueOf(i.intValue()+1);

進一步看Integer.valueOf():
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

所以 i++ 其實每次都是新創建了一個Integer對象,並把它的引用賦值給 i。

可以改爲
synchronized (instance) {
i++;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章