Java根底增強之多線程篇(線程創立與終止、互斥、通訊、本地變量)。本站提示廣大學習愛好者:(Java根底增強之多線程篇(線程創立與終止、互斥、通訊、本地變量))文章只能為提供參考,不一定能成為您想要的結果。以下是Java根底增強之多線程篇(線程創立與終止、互斥、通訊、本地變量)正文
public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {
/* What will be run. */
private Runnable target;
......
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
*/
public synchronized void start() {......}
......
@Override
public void run() {
if (target != null) {
target.run();
}
}
......
}
Thread類與Runnable接口都位於java.lang包中。從下面我們可以看出,Runnable接口中只定義了run()辦法,Thread類完成了Runnable 接口偏重寫了run()辦法。當調用Thread 類的start()辦法時,實踐上Java虛擬機就去調用Thread 類的run()辦法,而Thread 類的run()辦法中最終調用的是Runnable類型對象的run()辦法。
public class ThreadTest1 extends Thread {
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 1:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ThreadTest1 thread = new ThreadTest1 ();
thread.start();
}//main end
}
可以寫成外部類的方式,new Thread(){@Override run(...)}.start();
public class ThreadTest2 implements Runnable {
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread 3:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ThreadTest2 thread3 = new ThreadTest2();
Thread thread = new Thread(thread3);
thread.start();
}//main end
}
可以寫成外部類的方式,new Thread(new Runnable(){@Override run(...)}).start();
當調用Thread類的start()辦法時,將會創立一個線程,這時剛創立的線程處於就緒形態(可運轉形態),並沒有運轉,處於就緒形態的線程就可以等JVM調度。當JVM調度該線程時,該線程進入運轉形態,即執行Thread類的run()辦法中的內容。run()辦法執行完,線程完畢,線程進入死亡形態。這是線程自然終止的進程,我們也可以經過Thread類提供的一些辦法來終止線程。
stop()辦法沒有做任何的肅清操作就粗犷終止線程,釋放該線程所持有的對象鎖(下文將引見),受該對象鎖維護的其它對象對其他線程可見,因而具有不平安性。
suspend()辦法會使目的線程會停上去,但依然持有在這之前取得的對象鎖,對任何線程來說,假如它們想恢復目的線程,同時又試圖運用任何一個鎖定的資源,就會形成死鎖。
終上所述,不建議運用stop()辦法和suspend()辦法來終止線程,通常我們經過interrupt()辦法來終止處於阻塞形態和運轉形態的線程。
需求留意的是,interrupt()辦法不會中綴一個正在運轉的線程,僅僅是將線程的中綴標志設為true,當調用了阻塞辦法之後,線程會不時監聽中綴標志,假如為true,則發生一個InterruptedException異常,將InterruptedException放在catch中就能終止線程。
isInterrupted()辦法可以前往中綴標志,常用循環判別條件。
interrupted()辦法測試以後線程能否曾經中綴,線程的中綴標志由該辦法肅清。interrupted()除了前往中綴標志之外,它還會肅清中綴標志。
看上面例子
public class ThreadInterruptedTest extends Thread {
@Override
public void run() {
try {
int i = 0;
while(!isInterrupted()) {
i ++ ;
Thread.sleep(1000);
System.out.println(this.getName() + " is looping,i=" + i);
}
} catch (InterruptedException e) {
System.out.println(this.getName() +
" catch InterruptedException,state:" + this.getState());
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
ThreadInterruptedTest thread = new ThreadInterruptedTest();
System.out.println(thread.getName()
+ " state:" + thread.getState());
thread.start();
System.out.println(thread.getName()
+ " state:" + thread.getState());
Thread.sleep(5000);
System.out.println("flag: " + thread.isInterrupted());
//收回中綴指令
thread.interrupt();
System.out.println("flag: " + thread.isInterrupted());
System.out.println(thread.getName()
+ " state:" + thread.getState());
System.out.println(thread.interrupted());
}
}
運轉後果
Thread-0 state:NEW
Thread-0 state:RUNNABLE
Thread-0 is looping,i=1
Thread-0 is looping,i=2
Thread-0 is looping,i=3
Thread-0 is looping,i=4
flag: false
flag: true
Thread-0 state:TIMED_WAITING
Thread-0 catch InterruptedException,state:RUNNABLE
false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.itpsc.thread.ThreadInterruptedTest.run(ThreadInterruptedTest.java:11)
從運轉後果可以看出,調用interrupt() 收回中綴指令前,中綴標志位false,收回中綴指令後中綴標志位為true,而調用interrupted()辦法後則中綴標志被肅清。從收回的異常來看,是在一個sleep interrupted,且收回異常後線程被喚醒,以便線程能從異常中正常加入。
線程從創立到終止能夠會閱歷各種形態。在java.lang.Thread.State類的源碼中,可以看到線程有以下幾種形態:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。各種形態的轉換如下:
當經過Thread t = new Thread()方式創立線程時,線程處於新建形態;當調用t.start()辦法時,線程進入可運轉形態(留意,還沒有運轉);處於可運轉形態的線程將在適當的機遇被CPU資源調度器調度,進入運轉形態,也就是線程執行run()辦法中的內容;run()辦法執行完或許順序異常加入線程進入終止形態。線程從運轉形態也有能夠進入阻塞形態,如調用wait()辦法後進入等候對象鎖(下文將引見),調用sleep()辦法後停止入計時等候。
如今我們曾經知道線程的創立與終止了。互斥,是指零碎中的某些共享資源,一次只允許一個線程訪問,當一個線程正在訪問該臨界資源時,其它線程必需等候。
在java中,每一個對象有且僅有一個鎖,鎖也稱為對象監視器。經過對象的鎖,多個線程之間可以完成對某個辦法(臨界資源)的互斥訪問。那麼,如何獲取對象的鎖呢?當我們調用對象的synchronized修飾的辦法或許synchronized修飾的代碼塊時,鎖住的是對象實例,就獲取了該對象的鎖。
Java中有實例對象也有類對象,居然有對象鎖,那麼久有類鎖,也稱全局鎖。當synchronized修飾靜態辦法或許靜態代碼塊時,鎖住的是該類的Class實例(字節碼對象),獲取的便是該類的全局鎖。看上面獲取對象鎖完成線程互斥的兩種方式。
先看上面這個沒有完成線程互斥的例子。
public class SynchronizedTest {
public static void main(String[] args) {
new SynchronizedTest().init();
}
private void init() {
final Outputer output = new Outputer();
//線程1打印"hello,i am thread 1"
new Thread(new Runnable(){
@Override
public void run() {
while(true) {
try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
output.output("hello,i am thread 1");
}
}
}).start();
//線程2打印"hello,i am thread 2"
new Thread(new Runnable(){
@Override
public void run() {
while(true) {
try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
output.output("hello,i am thread 2");
}
}
}).start();
}
class Outputer {
public void output(String name) {
for(int i=0; i<name.length(); i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
運轉後果
hello,i am thread 1 hello,i am thread 2 hello,i am hellthread 1 o,i am thread 2 hello,i am thread 2 hello,i am thread 1 hello,i am thread 2 hello,i am threadhel 2lo,i am thread 1
線程1和線程2同時調用output辦法停止輸入,從運轉後果可以看出,線程之間沒有執行完各自的輸入義務就被交替了運轉了。上面經過對象的鎖完成線程1和線程2對output辦法的互斥訪問。
運用synchronized 對output辦法停止修飾,可以讓調用者取得鎖。synchronized 修飾辦法沒有顯示聲明鎖的對象,默許是以後辦法所在類的對象this。
public synchronized void output(String name) {
for(int i=0; i<name.length(); i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
運用synchronized 對output辦法中的代碼塊停止修飾,也可以讓調用者取得鎖。
public void output(String name) {
synchronized(this){
for(int i=0; i<name.length(); i++) {
System.out.print(name.charAt(i));
}
System.out.println();
}
}
運用synchronized之後,線程1和線程2對output辦法完成了互斥訪問。
hello,i am thread 1 hello,i am thread 2 hello,i am thread 1 hello,i am thread 2 hello,i am thread 1 hello,i am thread 2 hello,i am thread 1
先看上面的例子,我們來總結下synchronized的一些常用用法。
public class SynchronizedTest {
public static void main(String[] args) {
new SynchronizedTest().init();
}
private void init() {
final Outputer output = new Outputer();
//線程1打印"hello,i am thread 1"
new Thread(new Runnable(){
@Override
public void run() {
output.output("hello,i am thread 1");
}
}).start();
//線程2打印"hello,i am thread 2"
new Thread(new Runnable(){
@Override
public void run() {
output.output("hello,i am thread 2");
}
}).start();
}
static class Outputer {
public synchronized void output(String name) {
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name);
}
}
public void output2(String name) {
synchronized(this) {
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name);
}
}
}
public void output3(String name) {
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name);
}
}
public static synchronized void output4(String name) {
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name);
}
}
public void output5(String name) {
synchronized(Outputer.class) {
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name);
}
}
}
}
}
運轉後果
hello,i am thread 1 hello,i am thread 1 hello,i am thread 1 hello,i am thread 1 hello,i am thread 1 hello,i am thread 2 hello,i am thread 2 hello,i am thread 2 hello,i am thread 2 hello,i am thread 2
線程1和線程2同時訪問output 對象的synchronized 修飾的output 辦法,即兩個線程競爭的是output 對象的鎖,這是同一個鎖,所以當線程1在持有鎖的時分,線程2必需等候,即上面的用法1。
用法1
當一個線程訪問某個對象的synchronized 辦法或許synchronized 代碼塊時,其它線程對該對象的該synchronized 辦法或許synchronized 代碼塊的訪問將阻塞。
用法2
當一個線程訪問某個對象的synchronized 辦法或許synchronized 代碼塊時,其它線程對該對象的其他synchronized 辦法或許synchronized 代碼塊的訪問將阻塞。
修該下面的SynchronizedTest 例子,線程1訪問output辦法,線程2訪問output2 辦法,運轉後果同上,由於output辦法 和output2辦法都屬於同一個對象output ,因而線程1和線程2競爭的也是同一個鎖。
用法3
當一個線程訪問某個對象的synchronized 辦法或許synchronized 代碼塊時,其它線程依然可以對該對象的其他非synchronized 辦法或許synchronized 代碼塊訪問。
修該下面的SynchronizedTest 例子,線程1訪問output辦法,線程2訪問output3辦法,運轉後果是線程1和線程2交替輸入。後果不言而喻,線程2訪問output3辦法並不是synchronized 修飾的output 辦法或許代碼塊,線程2並不需求持有鎖,因而線程1的運轉不會阻塞線程2的運轉。
用法4
當synchronized 修飾靜態辦法時,鎖住的是該類的Class實例(字節碼對象)。修該下面的SynchronizedTest 例子,線程1訪問output4辦法,線程2訪問output5辦法,運轉後果同用法1,闡明線程1和線程2競爭的是Outputer類的Class實例(字節碼對象)的鎖。
多個線程之間往往需求互相協作來完成某一個義務,synchronized 和對象鎖能完成線程互斥,但是不能完成線程通訊。
線程之間的通訊經過java.lang包中Object類中的wait()辦法和notify()、notifyAll()等辦法停止。我們知道,Java中每個對象都有一個鎖,wait()辦法用於等候對象的鎖,notify()、notifyAll()辦法用於告訴其他線程對象鎖可以運用。
wait()\notify()\notifyAll()依賴於對象鎖,對象鎖是對象所持有,Object類是一切java類的父類,這樣每一個java類(對象)都有線程通訊的根本辦法。這就是這些辦法定義在Object類中而不定義在Thread類中的緣由。
wait()辦法的會讓以後線程釋放對象鎖並進入等候對象鎖的形態,以後線程是指正在cpu上運轉的線程。以後線程調用notify()\notifyAll()後,等候對象鎖的線程將被喚醒。
調用wait()辦法或許notify()辦法的對象必需和對象鎖所屬的對象是同一個對象,並且必需在synchronized辦法或許synchronized代碼塊中被調用。
yieId()的作用是給線程調度器一個提示,告知線程調度器以後線程情願讓出CPU,但是線程調度器可以疏忽這個提示。因而,yieId()的作用僅僅是告知線程調度器以後線程情願讓出CPU給其他線程執行(居然只是情願,以後線程可以隨時反悔,那其他線程也不一定能失掉CPU執行),而且不會讓以後線程釋放對象鎖。
yieId()能讓以後線程由運轉形態進入到就緒形態,從而讓其它具有相反優先級的等候線程獲取執行權。但是,並不能保證在以後線程調用yield()之後,其它具有相反優先級的線程就一定能取得執行權,也有能夠以後線程又進入到運轉形態持續運轉。
yieId()只建議在測試環境中運用。
wait()和yield()的區別
(1)wait()是讓線程由運轉形態進入到等候(阻塞)形態,而yield()是讓線程由運轉形態進入到就緒形態。
(2)wait()是讓線程釋放它所持有對象的鎖,而yield()辦法不會釋放鎖。
上面的例子是“主線程輸入三次接著子線程輸入三次”,反復兩次。
public class WaitnotifyTest {
public static volatile boolean shouldChildren = false;
public static void main(String[] args) throws Exception{
final Outputer outputer = new Outputer();
//創立子線程
Thread chrild = new Thread(new Runnable(){
@Override
public void run() {
try {
for(int i=0;i<2;i++)
outputer.children();
} catch (Exception e) {
e.printStackTrace();
}
}
});
chrild.start();
//主線程
for(int i=0;i<2;i++)
outputer.main();
}
}
class Outputer {
//子線程循環輸入
public synchronized void children() throws Exception{
while(!WaitnotifyTest.shouldChildren) {
System.out.println(Thread.currentThread().getName()
+ " thread end loop,go to waitting");
//子線程進入等候形態
this.wait();
}
System.out.println(Thread.currentThread().getName()
+ " thread start loop");
for(int i=1; i<=3; i++) {
System.out.println("hello,i am chrildren thread,loop:" + i);
}
WaitnotifyTest.shouldChildren = false;
//喚醒主線程
this.notify();
}
//主線程循環輸入
public synchronized void main() throws Exception{
while(WaitnotifyTest.shouldChildren) {
System.out.println(Thread.currentThread().getName()
+ " thread end loop,go to waitting");
//主線程進入等候形態
this.wait();
}
System.out.println(Thread.currentThread().getName()
+ " thread start loop");
for(int i=1; i<=3; i++) {
System.out.println("hello,i am main thread,loop:" + i);
}
WaitnotifyTest.shouldChildren = true;
//喚醒子線程
this.notify();
}
}
運轉後果
main thread start loop hello,i am main thread,loop:1 hello,i am main thread,loop:2 hello,i am main thread,loop:3 main thread end loop,go to waitting Thread-0 thread start loop hello,i am chrildren thread,loop:1 hello,i am chrildren thread,loop:2 hello,i am chrildren thread,loop:3 Thread-0 thread end loop,go to waitting main thread start loop hello,i am main thread,loop:1 hello,i am main thread,loop:2 hello,i am main thread,loop:3 Thread-0 thread start loop hello,i am chrildren thread,loop:1 hello,i am chrildren thread,loop:2 hello,i am chrildren thread,loop:3
volatile修飾shouldChildren,線程直接讀取shouldChildren變量並且不緩存它,修正了shouldChildren立馬讓其他線程可見,這就確保線程讀取到的變量是分歧的。
線程本地變量,能夠稱為線程部分變量更容易了解,即為每一個運用該變量的線程都提供一個變量值的正本,相當於將變量的正本綁定到線程中,每一個線程可以獨立地修正自己的變量正本,而不會和其它線程的變量正本抵觸。在線程消逝之後,線程部分變量的一切正本都會被渣滓回收(上面的源碼剖析中將提到)。
ThreadLocal
在java.lang.Thread類中,有一個ThreadLocal.ThreadLocalMap類型的變量threadLocals,這個變量就是用來存儲線程部分變量的。
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
上面我們重點剖析ThreadLocal的外部完成。ThreadLocal也位於java.lang包中。其次要成員有:
public T get() {}
private T setInitialValue() {}
public void set(T value) {}
private void remove(ThreadLocal key) {}
ThreadLocalMap getMap(Thread t){}
void createMap(Thread t, T firstValue) {}
static class ThreadLocalMap {}
Set
我們從set辦法開端。Set辦法源碼如下
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
先獲取以後的線程,然後經過getMap(t)辦法獲取到一個map,map的類型為ThreadLocalMap。
這個map其實就是存儲線程變量的對象threadLocals。ThreadLocalMap是ThreadLocal中的一個外部類,是一個定制的hashmap以便適用於存儲線程本地變量。居然是定制的hashmap,那麼就有Entry 和table(hashmap的外部完成參考上一篇:Java根底增強之集合篇(模塊記憶、精要剖析))。而ThreadLocalMap中的Entry 承繼了WeakReference,弱援用是不能保證不被渣滓回收器回收的,這就是前文提到的在線程消逝之後,線程部分變量的一切正本都會被渣滓回收。此外,Entry 中運用ThreadLocal作為key,線程部分變量作為value。假如threadLocals不為空,則設值否者調用createMap辦法創立threadLocals。留意設值的時分傳的是this而不是以後線程t。
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
接上去我們看看createMap辦法
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
* @param map the map to store.
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
createMap辦法其實就是為以後線程的threadLocals變量分配空間並存儲線程的第一個變量。如今我們曾經知道線程是如何初始化並設值自己的部分變量了,上面我們看看取值。
Get
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
先獲取以後的線程,然後經過getMap(t)辦法獲取以後線程存變量的對象threadLocals,假如threadLocals不為空則取值並前往(留意傳入的key是this對象而不是以後線程t),否則調用setInitialValue辦法初始化。setInitialValue和set辦法獨一不同的是調用了initialValue停止初始化,也就是在獲取變量之前要初始化。
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
總的來講,每創立一個線程(Thread對象),該線程即擁有存儲線程本地變量的threadLocals對象,threadLocals對象初始為null,當經過ThreadLocal對象調用set/get辦法時,就會對線程的threadLocals對象停止初始化,並且以以後ThreadLocal對象為鍵值,以ThreadLocal要保管的變量為value,存到threadLocals。看上面的例子。

public class ThreadLocalShareVariable {
public static void main(String[] args) {
//創立3個線程
for(int i=0; i<3;i++) {
//創立線程
new Thread(new Runnable(){
@Override
public void run() {
//線程設置自己的變量
int age = new Random().nextInt(100);
String name = getRandomString(5);
System.out.println("Thread " + Thread.currentThread().getName()
+ " has put data:" + name + " " + age);
//存儲與以後線程有關的變量
Passenger.getInstance().setName(name);
Passenger.getInstance().setAge(age);
//線程訪問共享變量
new ModuleA().getData();
new ModuleB().getData();
}
}).start();
}
}
static class ModuleA {
public void getData(){
//獲取與以後線程有關的變量
String name = Passenger.getInstance().getName();
int data = Passenger.getInstance().getAge();
System.out.println("moduleA get data from "
+ Thread.currentThread().getName() + ":" + name + " "+ data);
}
}
static class ModuleB {
public void getData(){
//獲取與以後線程有關的變量
String name = Passenger.getInstance().getName();
int data = Passenger.getInstance().getAge();
System.out.println("moduleB get data from "
+ Thread.currentThread().getName() + ":" + name + " "+ data);
}
}
/**
* 隨機生成字符串
* @param length
* @return
*/
public static String getRandomString(int length){
final String str = "abcdefghijklmnopqrstuvwxyz";
StringBuffer sb = new StringBuffer();
int len = str.length();
for (int i = 0; i < length; i++) {
sb.append(str.charAt(
(int) Math.round(Math.random() * (len-1))));
}
return sb.toString();
}
}
class Passenger {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Passenger(){}
//ThreadLocal存儲線程變量
public static ThreadLocal<Passenger> thsd = new ThreadLocal<Passenger>();
public static Passenger getInstance() {
//獲取以後線程范圍內的共享變量實例
Passenger passenger = thsd.get();
//懶漢形式創立實例
if(passenger == null) {
passenger = new Passenger();
thsd.set(passenger);
}
return passenger;
}
}
View Code
運轉後果

Thread Thread-1 has put data:vwozg 33 Thread Thread-2 has put data:hubdn 30 Thread Thread-0 has put data:mkwrt 35 moduleA get data from Thread-2:hubdn 30 moduleA get data from Thread-0:mkwrt 35 moduleA get data from Thread-1:vwozg 33 moduleB get data from Thread-1:vwozg 33 moduleB get data from Thread-0:mkwrt 35 moduleB get data from Thread-2:hubdn 30View Code
創立3個線程,每個線程要保管一個Passenger 對象,並且經過ModuleA 、ModuleB來訪問每個線程對應保管的Passenger 對象。
下面我們討論的是多線程之間如何訪問自己的變量。那麼多線程之間共享變量時如何的呢,看下的例子,線程1對共享變量停止減一操作,線程2對共享變量停止加2操作。

public class MutilThreadShareVariable {
static volatile int count = 100;
public static void main(String[] args) throws Exception{
final ShareDataDec sdDec = new ShareDataDec();
final ShareDataInc sdInc = new ShareDataInc();
//線程1
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++) {
sdDec.dec();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//線程2
new Thread(new Runnable(){
@Override
public void run() {
for(int i=0;i<5;i++) {
sdInc.inc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();;
}
static class ShareDataDec {
public synchronized void dec() {
count --;
System.out.println("Thread " + Thread.currentThread().getName()
+ " dec 1 from count,count remain " + count);
}
}
static class ShareDataInc {
public synchronized void inc() {
count = count + 2;
System.out.println("Thread " + Thread.currentThread().getName()
+ " inc 2 from count,count remain " + count);
}
}
}
View Code
運轉後果

Thread Thread-0 dec 1 from count,count remain 99 Thread Thread-1 inc 2 from count,count remain 101 Thread Thread-0 dec 1 from count,count remain 100 Thread Thread-1 inc 2 from count,count remain 102 Thread Thread-0 dec 1 from count,count remain 101 Thread Thread-1 inc 2 from count,count remain 103 Thread Thread-0 dec 1 from count,count remain 102 Thread Thread-1 inc 2 from count,count remain 104 Thread Thread-0 dec 1 from count,count remain 103 Thread Thread-1 inc 2 from count,count remain 105View Code
線程共享變量,只需對要對共享變量停止修正的代碼停止同步即可。