前記:師夷長技以自強
1.用輪詢實現的線程間通信機制
如下:
import java.util.ArrayList;
import java.util.List;
class MyList{
private List list = new ArrayList();
public void add(){
list.add("haha");
}
public int size(){
return list.size();
}
}
class ThreadA extends Thread{
volatile private MyList list;
public ThreadA(MyList list) {
this.list = list;
}
@Override
public void run() {
try{
for (int i = 0; i < 10; i++) {
list.add();
System.out.println("添加了"+(i+1)+"個元素");
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
volatile private MyList list;
public ThreadB(MyList list) {
this.list = list;
}
@Override
public void run() {
try{
while (true){
if(list.size()==5){
System.out.println("==5,ThreadB exits!!!");
throw new InterruptedException();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
MyList myList = new MyList();
ThreadA a = new ThreadA(myList);
a.setName("A");
a.start();
ThreadB b = new ThreadB(myList);
b.setName("B");
b.start();
}
}
output:
添加了1個元素
添加了2個元素
添加了3個元素
添加了4個元素
添加了5個元素
==5,ThreadB exits!!!
java.lang.InterruptedException
at ThreadB.run(Test.java:48)
添加了6個元素
添加了7個元素
添加了8個元素
添加了9個元素
添加了10個元素
在線程A添加了5個元素後,線程B檢測到了符合的條件然後拋出異常退出。然而,在這種情況下線程B是沒有讓出CPU在等待的。
2.wait和notify
2.1基本使用
wait函數是讓當前線程暫停運行,notify函數是使等待當前鎖的線程重新獲得cpu運行。需要注意的是,wait和notify在被調用之前都要獲取到相應的鎖,也就是和如下:
class MyThread1 extends Thread{
private Object lock;
public MyThread1(Object lock) {
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock){
System.out.println("start wait time="+System.currentTimeMillis());
lock.wait();
System.out.println("end wait time="+System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class MyThread2 extends Thread{
private Object lock;
public MyThread2(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
System.out.println("start notify time="+System.currentTimeMillis());
lock.notify();
System.out.println("end notify time="+System.currentTimeMillis());
}
}
}
public class Test {
public static void main(String[] args) {
try{
Object lock = new Object();
MyThread1 t1 = new MyThread1(lock);
t1.start();
Thread.sleep(3000);
MyThread2 t2 = new MyThread2(lock);
t2.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
output:
start wait time=1591601206518
start notify time=1591601209523
end notify time=1591601209526
end wait time=1591601209528
可以看到,線程1停止三分鐘後被線程2通知後繼續運行。
2.2把第一個案例改爲wait和notify實現
import java.util.ArrayList;
import java.util.List;
class MyList{
private static List list = new ArrayList();
public static void add(){
list.add("haha");
}
public static int size(){
return list.size();
}
}
class ThreadA extends Thread{
private Object lock;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
try{
synchronized (lock){
//if(MyList.size()!=5){
System.out.println("wait begin "+System.currentTimeMillis());
lock.wait();
System.out.println("wait end"+System.currentTimeMillis());
// }
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private Object lock;
public ThreadB(Object lock) {
this.lock = lock;
}
@Override
public void run() {
try{
synchronized (lock){
for (int i = 0; i < 10; i++) {
MyList.add();
if (MyList.size()==5){
lock.notify();
System.out.println("sended message");
}
System.out.println("add "+(i+1)+"elements");
Thread.sleep(1000);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try{
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(50);
ThreadB b = new ThreadB(lock);
b.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
output:
wait begin 1591604846284
add 1elements
add 2elements
add 3elements
add 4elements
sended message
add 5elements
add 6elements
add 7elements
add 8elements
add 9elements
add 10elements
wait end1591604856392
線程A剛開始獲取鎖然後調用wait釋放鎖,線程B得到鎖運行,並在添加第5個元素的時候喚醒了線程A,從而得到以上的運行效果。處理notify方法外,還有一個notifyAll方法。前者是僅僅通知等待隊列中的一個線程,後者是通知因同一個資源而進入等待隊列的進程,然後根據優先級選擇。
每個鎖對象都有兩個隊列,一個是就緒隊列,一個是阻塞隊列。就緒隊列存儲了將要獲得鎖的線程,阻塞隊列存儲了被阻塞的線程。線程被喚醒進入就緒隊列準備被執行調度,線程被wait則進入阻塞隊列等待下一次的喚醒。
2.3wait釋放鎖而notify不釋放鎖
wait被調用後當前線程將釋放鎖,但是notify將不會釋放,線程執行完同步代碼塊後才釋放鎖。
2.4 線程調用了wait方法後,再調用interruput方法會出現InterrruptedException異常
class Service{
public void testMethod(Object lock){
try {
synchronized (lock){
System.out.println("begin wait()");
lock.wait();
System.out.println("end wait");
}
}catch (InterruptedException e){
e.printStackTrace();
System.out.println("wait thread interrupted!!!");
}
}
}
class ThreadA extends Thread{
private Object lock;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
public class Test {
public static void main(String[] args) {
try{
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(5000);
a.interrupt();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
output:
begin wait()
java.lang.InterruptedException
wait thread interrupted!!!
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Object.wait(Object.java:516)
at Service.testMethod(Test.java:6)
at ThreadA.run(Test.java:26)
可見,線程A調用了wait方法後如果執行interrupt方法,就會引發InterruptedException異常。
2.5 notify只通知一個線程
class Service{
public void testMethod(Object lock){
try {
synchronized (lock){
System.out.println("begin wait() ThreadName="+Thread.currentThread().getName());
lock.wait();
System.out.println(" end wait() ThreadName="+Thread.currentThread().getName());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class ThreadA extends Thread{
private Object lock;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
class ThreadB extends Thread{
private Object lock;
public ThreadB(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
class ThreadC extends Thread{
private Object lock;
public ThreadC(Object lock) {
this.lock = lock;
}
@Override
public void run() {
Service service = new Service();
service.testMethod(lock);
}
}
class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
lock.notify();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
ThreadB b = new ThreadB(lock);
b.start();
ThreadC c = new ThreadC(lock);
c.start();
Thread.sleep(1000);
NotifyThread notifyThread = new NotifyThread(lock);
notifyThread.start();
}
}
output:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
如果在把通知線程的代碼改爲如下
class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
lock.notify();
lock.notify();
lock.notify();
lock.notify();
lock.notify();
lock.notify();
}
}
}
output:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
end wait() ThreadName=Thread-2
end wait() ThreadName=Thread-1
2.6 notifyAll喚醒所有線程
把上例的通知線程改爲如下即可
class NotifyThread extends Thread{
private Object lock;
public NotifyThread(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
lock.notifyAll();
}
}
}
output:
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-1
end wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
2.7 wait(long)
如果在指定的時間內沒有線程喚醒當前線程,則超過這個時間則自動喚醒。
class MyRunnable{
static private Object lock = new Object();
static private Runnable runnable1 = new Runnable() {
@Override
public void run() {
try{
synchronized (lock){
System.out.println("wait begin timer="+System.currentTimeMillis());
lock.wait(5000);
System.out.println("wait end timer="+System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
};
public static void main(String[] args) {
Thread thread = new Thread(runnable1);
thread.start();
}
}
output:
wait begin timer=1591623207205
wait end timer=1591623212285
2.8wait/notify模式注意點
notify提前如果其他線程的notify比當前線程更早,則notify是無效的。
wait等待的條件發生變化時我們知道wait線程可以被喚醒,也可以超時自動喚醒,那當線程被喚醒後其所等待的添加發生變化時也會引起異常。比如兩個線程同時刪除一個集合的元素,開始集合爲空兩個刪除線程都阻塞。後來另一個線程往集合放一個元素,會造成兩個線程執行刪除,而因爲只有一個元素則必定會引發異常。
3 生產者消費者模式
3.1當只有一個生產者和消費者時
此時只要notify就可以完成對異類線程的喚醒。如下:
class P {
private String lock;
public P(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
while (!ValueObject.value.equals("")) {
System.out.println("P "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("P "+Thread.currentThread().getName()+" runnable!");
String value = System.currentTimeMillis() + "_" + System.nanoTime();
ValueObject.value = value;
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class C {
private String lock;
public C(String lock) {
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
while (ValueObject.value.equals("")) {
System.out.println("C "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("C "+Thread.currentThread().getName()+" runnable!");
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ValueObject {
public static String value = "";
}
class ThreadP extends Thread {
private P p;
public ThreadP(P p) {
this.p = p;
}
@Override
public void run() {
while (true) {
p.setValue();
}
}
}
class ThreadC extends Thread {
private C r;
public ThreadC(C r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.getValue();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
ThreadP[] pThread = new ThreadP[2];
ThreadC[] cThread = new ThreadC[2];
for (int i = 0; i < 1; i++) {
pThread[i] = new ThreadP(p);
pThread[i].setName("P"+(i+1));
cThread[i] = new ThreadC(c);
cThread[i].setName("C"+(i+1));
pThread[i].start();
cThread[i].start();
}
Thread.sleep(5000);
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName()+" "+threadArray[i].getState());
}
}
}
output:(部分)
P P1 waiting!
C C1 runnable!
P P1 runnable!
P P1 waiting!
C C1 runnable!
P P1 runnable!
P P1 waiting!
C C1 runnable!
可以看出,生產者和消費者可以同步進行。
3.2多生產者和多消費者時
如果每次僅僅喚醒一個線程,那麼可能只喚醒了同類線程,等待的線程越來越多,最後造成程序的假死。
class P {
private String lock;
public P(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
while (!ValueObject.value.equals("")) {
System.out.println("P "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("P "+Thread.currentThread().getName()+" runnable!");
String value = System.currentTimeMillis() + "_" + System.nanoTime();
ValueObject.value = value;
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class C {
private String lock;
public C(String lock) {
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
while (ValueObject.value.equals("")) {
System.out.println("C "+Thread.currentThread().getName()+" waiting!");
lock.wait();
}
System.out.println("C "+Thread.currentThread().getName()+" runnable!");
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ValueObject {
public static String value = "";
}
class ThreadP extends Thread {
private P p;
public ThreadP(P p) {
this.p = p;
}
@Override
public void run() {
while (true) {
p.setValue();
}
}
}
class ThreadC extends Thread {
private C r;
public ThreadC(C r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.getValue();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
ThreadP[] pThread = new ThreadP[2];
ThreadC[] cThread = new ThreadC[2];
for (int i = 0; i < 2; i++) {
pThread[i] = new ThreadP(p);
pThread[i].setName("P"+(i+1));
cThread[i] = new ThreadC(c);
cThread[i].setName("C"+(i+1));
pThread[i].start();
cThread[i].start();
}
Thread.sleep(5000);
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName()+" "+threadArray[i].getState());
}
}
}
output:(部分)
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
C C2 runnable!
P P1 runnable!
P P1 waiting!
P P2 waiting!
C C1 runnable!
C C1 waiting!
P P1 runnable!
P P1 waiting!
P P2 waiting!
C C2 runnable!
C C2 waiting!
C C1 waiting!
main RUNNABLE
Monitor Ctrl-Break RUNNABLE
P1 WAITING
C1 WAITING
P2 WAITING
C2 WAITING
經過若干次的調用,最後所有的生產者和消費者都進入了等待狀態。解決的辦法也很簡單,就是把P.java和C.java中的notify改爲notifyAll。
4.管道通信
4.1字節流
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
class WriteData{
public void writeMethod(PipedOutputStream out){
try{
System.out.println("write :");
for (int i = 0; i < 300; i++) {
String outData = "" + (i+1);
out.write(outData.getBytes());
System.out.print(outData);
}
System.out.println();
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
class ReadData{
public void readMethod(PipedInputStream input){
try{
System.out.println("read :");
byte[] byteArray = new byte[20];
int readLength = input.read(byteArray);
while (readLength != -1){
String newData = new String(byteArray, 0, readLength);
System.out.print(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ThreadWrite extends Thread{
private WriteData write;
private PipedOutputStream out;
public ThreadWrite(WriteData write, PipedOutputStream out) {
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
class ThreadRead extends Thread{
private ReadData read;
private PipedInputStream input;
public ThreadRead(ReadData read, PipedInputStream input) {
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
public class Test{
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedInputStream inputStream = new PipedInputStream();
PipedOutputStream outputStream = new PipedOutputStream();
// outputStream.connect(inputStream);
inputStream.connect(outputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.2字符流
import java.io.*;
class WriteData{
public void writeMethod(PipedWriter out){
try{
System.out.println("write :");
for (int i = 0; i < 300; i++) {
String outData = "" + (i+1);
out.write(outData);
System.out.print(outData);
}
System.out.println();
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
class ReadData{
public void readMethod(PipedReader input){
try{
System.out.println("read :");
char[] byteArray = new char[20];
int readLength = input.read(byteArray);
while (readLength != -1){
String newData = new String(byteArray, 0, readLength);
System.out.print(newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ThreadWrite extends Thread{
private WriteData write;
private PipedWriter out;
public ThreadWrite(WriteData write, PipedWriter out) {
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
class ThreadRead extends Thread{
private ReadData read;
private PipedReader input;
public ThreadRead(ReadData read, PipedReader input) {
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
public class Test{
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedReader inputStream = new PipedReader();
PipedWriter outputStream = new PipedWriter();
// outputStream.connect(inputStream);
inputStream.connect(outputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5.join方法
5.1join方法的基本使用
join方法使所屬的線程對象x正常執行run方法中的任務,使當前線程z進行無限期阻塞,等待線程x銷燬後再繼續執行線程z後面的代碼。
class MyThread extends Thread{
@Override
public void run() {
try{
int secondValue = (int) (Math.random() * 10000);
System.out.println(secondValue);
Thread.sleep(secondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
MyThread threadTest = new MyThread();
threadTest.start();
threadTest.join();
System.out.println("haha");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
4603
haha
5.2 join方法與interrrupt
class ThreadA extends Thread{
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String newString = new String();
Math.random();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
ThreadA threadA = new ThreadA();
threadA.start();
threadA.join();
System.out.println("ThreadB run end!");
} catch (InterruptedException e) {
System.out.println("ThreadB run catch!");
e.printStackTrace();
}
}
}
class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.interrupt();
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadB b = new ThreadB();
b.start();
Thread.sleep(500);
ThreadC c = new ThreadC(b);
c.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
ThreadB run catch!
java.lang.InterruptedException
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Thread.join(Thread.java:1353)
at java.base/java.lang.Thread.join(Thread.java:1427)
at ThreadB.run(Test.java:17)
當線程B在調用了join後被線程C interrupt,會發生異常,但線程按鈕還是呈“紅色”,因爲線程A還在繼續運行中,沒有發生異常。
5.3 join(long)方法
class MyThread extends Thread{
@Override
public void run() {
try {
System.out.println("begin Timer="+System.currentTimeMillis());
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
MyThread threadTest = new MyThread();
threadTest.start();
threadTest.join(2000);
System.out.println(" end timer="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
begin Timer=1591694019386
end timer=1591694021385
可以看到,運行的效果是等待了2秒鐘。
5.4join(long)和sleep(join)的區別
閱讀join的源碼發現裏面是使用wait實現的,因此join別調用後是可以釋放鎖的。而sleep方法被調用後卻不釋放鎖。
class ThreadA extends Thread{
private ThreadB b;
public ThreadA(ThreadB b) {
this.b = b;
}
@Override
public void run() {
try {
synchronized (b){
b.start();
Thread.sleep(6000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
System.out.println(" b run begin timer="+System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(" b run end timer="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void bService(){
System.out.println("bService timer"+System.currentTimeMillis());
}
}
class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.bService();
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadB b = new ThreadB();
ThreadA a = new ThreadA(b);
a.start();
Thread.sleep(1000);
ThreadC c = new ThreadC(b);
c.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
b run begin timer=1591706030617
b run end timer=1591706035720
bService timer1591706036618
可見,bService是過了6秒後被調用的,也就是說線程A在調用了Thread.sleep(6000)後沒有釋放線程B對象的鎖,導致其同步方法bService不能被線程C立即調用。而join是可以釋放鎖的,如下
class ThreadA extends Thread{
private ThreadB b;
public ThreadA(ThreadB b) {
this.b = b;
}
@Override
public void run() {
try {
synchronized (b){
b.start();
b.join();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
System.out.println(" b run begin timer="+System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(" b run end timer="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void bService(){
System.out.println("bService timer"+System.currentTimeMillis());
}
}
class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.bService();
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadB b = new ThreadB();
ThreadA a = new ThreadA(b);
a.start();
Thread.sleep(1000);
ThreadC c = new ThreadC(b);
c.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
b run begin timer=1591706475496
bService timer1591706476497
b run end timer=1591706480580
6.ThreadLocal使用
ThreadLocal類主要解決每個線程綁定自己的值,變量在不同線程間的隔離性。
public class Test{
public static ThreadLocal t1 = new ThreadLocal();
public static void main(String[] args) {
if(t1.get() == null){
System.out.println("no value!");
t1.set("my value");
}
System.out.println(t1.get());
System.out.println(t1.get());
}
}
output:
no value!
my value
my value
可以通過set和get對ThreadLocal操作。
6.1 驗證線程變量的隔離性
class Tools{
public static ThreadLocal t1 = new ThreadLocal();
}
class ThreadA extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
Tools.t1.set("ThreadA"+i);
System.out.println("ThreadA get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
Tools.t1.set("ThreadB"+i);
System.out.println("ThreadB get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
for (int i = 0; i < 100; i++) {
Tools.t1.set("Main"+i);
System.out.println("Main get Value="+Tools.t1.get());
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:(部分)
Main get Value=Main92
ThreadA get Value=ThreadA93
ThreadB get Value=ThreadB93
Main get Value=Main93
ThreadA get Value=ThreadA94
ThreadB get Value=ThreadB94
Main get Value=Main94
ThreadA get Value=ThreadA95
ThreadB get Value=ThreadB95
Main get Value=Main95
ThreadA get Value=ThreadA96
ThreadB get Value=ThreadB96
Main get Value=Main96
ThreadA get Value=ThreadA97
ThreadB get Value=ThreadB97
Main get Value=Main97
ThreadA get Value=ThreadA98
ThreadB get Value=ThreadB98
Main get Value=Main98
ThreadA get Value=ThreadA99
ThreadB get Value=ThreadB99
Main get Value=Main99
可見,這三個線程都能取出屬於自己的值,ThreadLocal類對每個線程的存儲是具有隔離性的。
6.2解決ThreadLocal中值爲null
可以創建一個繼承自ThreadLocal的類ThreadLocalEx,重寫其initialValue方法
import java.util.Date;
class ThreadLocalEx extends ThreadLocal{
@Override
protected Object initialValue() {
return new Date().getTime();
}
}
class Tools{
public static ThreadLocalEx t1 = new ThreadLocalEx();
}
class ThreadA extends Thread{
@Override
public void run() {
try{
for (int i = 0; i < 10; i++) {
System.out.println("ThreadA value="+Tools.t1.get());
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
try {
for (int i = 0; i < 10; i++) {
System.out.println("Main value="+Tools.t1.get());
Thread.sleep(100);
}
Thread.sleep(5000);
ThreadA a = new ThreadA();
a.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
output:
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
可以看到,Main和ThreadA中都有自己的默認值了。
7.InheritableThreadLocal類
該類用法與ThreadLocal類似,只是子線程可以繼承從父線程中的值。
8.總結
本文主要說明了線程之間是如何通信的,其中等待通知是基本的模式,還有生產者消費者,管道等通信方式,join方法的使用等。