|-目錄
|??線程間通訊
??-|wait與notify方式
??-|等待(join)方式
??-|管道(pipeStream)方式
|??-線程共享變量【ThreadLocal】
-線程間通訊
?1.線程間通訊-wait與notify方式
??線程間通訊:調(diào)用wait方法前困檩,首先獲得wait對象的鎖。執(zhí)行wait方法后码泞,當前線程釋放鎖逛薇,并且狀態(tài)變更為Waiting狀態(tài),當前任務加入至“預處理隊列”中外里;調(diào)用notify方法前怎爵,首先獲得notify對象的鎖。執(zhí)行完notify synchronized同步代碼塊后盅蝗,當前線程釋放鎖鳖链,如果該線程沒有任務則該線程狀態(tài)變更為Terminated狀態(tài)。
public class ThreadWaitNotifyStateDemo {
public static void main(String[] args) throws InterruptedException{
final ServiceWaitNotify service = new ServiceWaitNotify();
Thread thread_1 = new Thread("thread_1") {
public void run() {
try {
service.testMethodA();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
};
Thread thread_2 = new Thread("thread_2") {
public void run() {
try {
service.testMethodB();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
};
thread_1.start();
thread_2.start();
Thread.sleep(1100);
System.out.println(thread_1.getName() + ",state:" + thread_1.getState());
Thread.sleep(500);
System.out.println(thread_1.getName() + ",state:" + thread_1.getState());
}
}
class ServiceWaitNotify {
public void testMethodA() throws InterruptedException {
Thread.sleep(1000);
synchronized (this) {
System.out.println(Thread.currentThread().getName() + ",進入Wait!" + Thread.currentThread().getState());
this.wait();
System.out.println(Thread.currentThread().getName() + ",退出Wait!" + Thread.currentThread().getState());
}
}
public void testMethodB() throws InterruptedException {
Thread.sleep(1500);
synchronized (this) {
System.out.println(Thread.currentThread().getName() + ",進入notify!" + Thread.currentThread().getState());
this.notify();
System.out.println(Thread.currentThread().getName() + ",退出notify!" + Thread.currentThread().getState());
}
}
}
??以上代碼:介紹了wait與notify的簡單使用墩莫,使用這兩個方法時必須有同種鎖的同步代碼塊芙委。同時說明了線程執(zhí)行相關方法時,線程所處狀態(tài)狂秦。如【圖1-2】可以看出當線程執(zhí)行wait方法后所處狀態(tài)為WATING,當notify線程同步代碼塊執(zhí)行完后灌侣,wait線程進入RUNNABLE狀態(tài),等待wait線程任務執(zhí)行完后進入TERMINATED狀態(tài)故痊。
?2.線程間通訊-等待(join)方式
??一個線程通過等待的方式顶瞳,獲取另一個線程數(shù)據(jù)執(zhí)行的結果。
public class ThreadJoinDemo {
public static void main(String[] args) {
try {
JoinThread thread = new JoinThread("thread_1");
thread.start();
thread.join();
System.out.println("" + Thread.currentThread().getName() + ",獲得["
+ thread.getName() + "],value:" + thread.value);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class JoinThread extends Thread {
public int value;
public JoinThread (String name) {
super(name);
}
@Override
public void run() {
System.out.println("" + Thread.currentThread().getName() + ",開始運行!");
try {
Thread.sleep(1000);
value = 5;
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("" + Thread.currentThread().getName() + ",結束運行!");
}
}
??從【圖1-3】可以看出愕秫,join方法是等待子線程全部執(zhí)行完后慨菱,主線程才往下執(zhí)行,這樣主線程就能獲取子線程修改后的值戴甩。
??這里要產(chǎn)生一個懷疑點例获,join方法的柱塞方式是否與wait方法相似妇多?
能夠產(chǎn)生上述懷疑的有兩點:
?1)join方法會throws InterruptedException【
InterruptedException說明
:線程在活動前或活動中啼辣,處于等待或睡眠狀態(tài)時粥喜,該線程被異常中斷則報上述異常。】缴川,故該異常必定與sleep或wait方法有關聯(lián)茉稠。?2)join方法等待屬于不確定的等待,故join底層不可能使用sleep把夸。
??驗證:
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底層就是使用wait方法,并且wait方法使用的鎖是線程對象本身“蚶海【這樣可以驗證調(diào)用join方法是否與wait方法一樣會釋放當前鎖對象】
public class ThreadJoinDemo {
public static void main(String[] args) throws InterruptedException{
JoinThread thread = new JoinThread("thread_1");
synchronized (thread) {
System.out.println("" + Thread.currentThread().getName() + ",獲得thread鎖!");
thread.start();
Thread.sleep(1000);
System.out.println("" + Thread.currentThread().getName() + ",調(diào)用thread.join()");
thread.join();
System.out.println("" + Thread.currentThread().getName() + ",結束!");
}
}
}
class JoinThread extends Thread {
public int value;
public JoinThread (String name) {
super(name);
}
@Override
public void run() {
synchronized (this) {
System.out.println("" + Thread.currentThread().getName() + ",開始運行!");
value = 5;
}
System.out.println("" + Thread.currentThread().getName() + ",結束運行!");
}
}
??以上代碼可以看出嘹狞,如果join不能釋放鎖,則JoinThread run方法獲取不了鎖也執(zhí)行不了方法體中程序誓竿。但是【圖1-4】完全推翻了上述‘如果假設’磅网,證明了join底層就是wait,調(diào)用join方法與wait方法一樣會釋放當前鎖筷屡。
?3.線程間通訊-管道(pipeStream)方式
??兩個線程不借助文本涧偷,使用管道【輸出管道-寫數(shù)據(jù),輸入管道-讀數(shù)據(jù)】方式直接進行數(shù)據(jù)傳輸速蕊。
?管道通訊兩種方式:
?1)
PipedInputStream
和PipedOutputStream
?2)
PipedReader
和PipedWriter
?管道通訊使用注意點:
?1)管道通訊必須建立連接嫂丙。src.connect(snk)/snk.connect(src)【src:未連接的管道輸出流,snk:未鏈接的管道輸入流】规哲。
?2)PipedInputStream和PipedOutputStream不能在同一個線程使用》瘫恚【會出現(xiàn)死鎖現(xiàn)象】
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class ThreadPipeStreamDemo {
public static void main(String[] args) throws IOException {
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
out.connect(in);
ThreadWrite write = new ThreadWrite("write_thread", out);
ThreadRead read = new ThreadRead("read_thread", in);
write.start();
read.start();
}
}
class ThreadWrite extends Thread {
private PipedOutputStream outputStream;
public ThreadWrite (String name, PipedOutputStream out) {
super(name);
this.outputStream = out;
}
@Override
public void run() {
if (null == outputStream) return;
try {
System.out.println("" + Thread.currentThread().getName() + ",開始寫入數(shù)據(jù)!");
for(int i = 0; i < 10; i++) {
String writeData = "" + (i + 1);
outputStream.write(writeData.getBytes(), 0 ,writeData.getBytes().length);
}
outputStream.flush();
outputStream.close();
System.out.println("" + Thread.currentThread().getName() + ",完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ThreadRead extends Thread {
private PipedInputStream inputStream;
public ThreadRead (String name, PipedInputStream in) {
super(name);
this.inputStream = in;
}
@Override
public void run() {
if (null == inputStream) return;
byte[] buffer = new byte[1048];
System.out.println("" + Thread.currentThread().getName() + ",開始讀數(shù)據(jù)!");
String data = "";
try {
int length = inputStream.read(buffer, 0, buffer.length);
while (length != -1) {
String str = new String(buffer, 0, length);
data += str;
length = inputStream.read(buffer, 0, buffer.length);
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("" + Thread.currentThread().getName() + ",數(shù)據(jù):" + data);
}
}
-線程共享變量【ThreadLocal】
??ThreadLocal類提供了線程局部(thread-local)變量唉锌,ThreadLocal與普通變量的區(qū)別在于:1,每個線程持有的是變量初始化副本的弱引用【直譯:每個線程都有自己的局部變量】竿奏。2袄简,ThreadLocal與線程生命周期相關聯(lián)【線程消失之后,其線程局部實例的所有副本都會被垃圾回收】泛啸。
public class ThreadLocalDemo {
public static void main(String[] args) {
final Tools tools = new Tools();
Thread thread_1 = new Thread(new Runnable() {
public void run() {
int num = tools.getUid();
System.out.println("" + Thread.currentThread().getName() + ",num:" + num);
}
}, "thread_1");
Thread thread_2 = new Thread(new Runnable() {
public void run() {
tools.setUid(10);
int num = tools.getUid();
System.out.println("" + Thread.currentThread().getName() + ",num:" + num);
}
}, "thread_2");
thread_1.start();
thread_2.start();
}
}
class Tools {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
protected Integer initialValue() {//ThreadLocal 獲取初始默認值
return 0;
};
};
public int getUid() {
return threadLocal.get().intValue(); //ThreadLocal get()方法獲取當前變量值
}
public void setUid(int num) {
threadLocal.set(num); //ThreadLocal set()方法設置當前變量值
}
}
??從結果可以看出绿语,同一個ThreadLocal靜態(tài)變量但是獲取出來的值不同,這就解釋了上面局部變量的定義候址。
-總結
??這里主要講解線程間通訊吕粹,以及線程共享變量簡單使用。