題目
面試題如下:
考點
本題考查的是線程通信的問題。更側重的是考查sync+wait+notify的組合使用。
代碼
Solution1: LockSupport
比較優的解法。
import java.util.concurrent.locks.LockSupport;
public class Maureen_LockSupport {
static Thread t1 = null, t2 = null;
public static void main(String[] args) throws Exception {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
t1 = new Thread(() -> {
for (char c : aI) {
System.out.print(c);
LockSupport.unpark(t2); // t2線程繼續運行
LockSupport.park(); // 當前線程暫停
}
}, "t1");
t2 = new Thread(() -> {
for (char c : aC) {
LockSupport.park();
System.out.print(c);
LockSupport.unpark(t1);
}
}, "t2");
t1.start();
t2.start();
}
}
運行結果:1A2B3C4D5E6F7G
Solution2:CAS -- 自旋鎖
public class Maureen_CAS {
enum ReadyToRun {
T1, T2
}
static volatile ReadyToRun r = ReadyToRun.T1;
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
for (char c : aI) {
while (r != ReadyToRun.T1) {
//空轉,一直佔着CPU,也就是自旋(原地打轉)
}
System.out.print(c);
r = ReadyToRun.T2; //每輸出一個字符,就將r標記進行修改
}
}, "t1").start();
new Thread(() -> {
for (char c : aC) {
while (r != ReadyToRun.T2) {
}
System.out.print(c);
r = ReadyToRun.T1;
}
}, "t2").start();
}
}
輸出結果:1A2B3C4D5E6F7G
Solution3:AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
public class Maureen_AtomicInteger {
static AtomicInteger threadNo = new AtomicInteger(1);
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
for (char c : aI) {
while (threadNo.get() != 1) {
}
System.out.print(c);
threadNo.set(2);
}
}, "t1").start();
new Thread(() -> {
for (char c : aC) {
while (threadNo.get() != 2) {
}
System.out.print(c);
threadNo.set(1);
}
}, "t2").start();
}
}
輸出結果:1A2B3C4D5E6F7G
Solution4:BlockingQueue
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Maureen_BlockingQueue {
static BlockingQueue<String> q1 = new ArrayBlockingQueue(1);
static BlockingQueue<String> q2 = new ArrayBlockingQueue(1);
public static void main(String[] args) throws Exception {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
for (char c : aI) {
System.out.print(c);
try {
q1.put("ok");
q2.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
new Thread(() -> {
for (char c : aC) {
try {
q1.take(); //等着q1中有內容,取出內容再繼續運行;如果沒有內容,就阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(c);
try {
q2.put("ok");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2").start();
}
}
輸出結果:1A2B3C4D5E6F7G
Solution5:PipedStream
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class Maureen_PipedStream {
public static void main(String[] args) throws IOException {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
PipedInputStream input1 = new PipedInputStream();
PipedInputStream input2 = new PipedInputStream();
PipedOutputStream output1 = new PipedOutputStream();
PipedOutputStream output2 = new PipedOutputStream();
input1.connect(output2);
input2.connect(output1);
String msg = "Your Turn";
new Thread(() -> {
byte[] buffer = new byte[9];
try {
for (char c : aI) {
input1.read(buffer);
if (new String(buffer).equals(msg)) {
System.out.print(c);
}
output1.write(msg.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}, "t1").start();
new Thread(() -> {
byte[] buffer = new byte[9];
try {
for (char c : aC) {
System.out.print(c);
output2.write(msg.getBytes());
input2.read(buffer);
if (new String(buffer).equals(msg)) {
continue;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}, "t2").start();
}
}
輸出結果:A1B2C3D4E5F6G7。該方法效率很低。
Solution6:sync_wait_notify
①兩個線程交替輸出,但是無法確保哪個線程先輸出,可能會先輸出字母
//要使用wait和notify,要對一個對象加鎖,才能在其中進行notify和wait的操作
public class Maureen_sync_wait_notify_00 {
public static void main(String[] args) {
final Object o = new Object();
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
synchronized (o) {// 不能使用this,因爲代碼塊中是一個匿名內部類,如果用this,那麼和下面代碼塊的this不是同一個對象,不能構成同步
for (char c : aI) {
System.out.print(c);
try {
o.notify(); // 叫醒等待隊列裏的任意一個,notifyAll是叫醒所有線程
o.wait(); // 讓出鎖 運行中的線程進入等待隊列中,與此同時將鎖釋放
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify(); // 必須,否則無法停止程序。因爲無論哪個線程先運行完,總有個線程處於wait狀態,只有叫醒該線程程序纔會運行完
}
}, "t1").start();
new Thread(() -> {
synchronized (o) {// 拿不到鎖的時候就在等待隊列中
for (char c : aC) {
System.out.print(c);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t2").start();
}
}
輸出結果:1A2B3C4D5E6F7G
執行流程如下:
持有鎖之後就執行輸出。
②使用CAS限制t2線程先運行
//sync_wait_notify程序無法限制哪個線程先運行,因此在這個程序裏限制哪個線程先運行
public class Maureen_sync_wait_notify_01 {
private static volatile boolean t2Started = false;
public static void main(String[] args) {
final Object o = new Object();
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
synchronized (o) {
while (!t2Started) { //限制t2先運行,如果t2沒有先運行,t1就先等待
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (char c : aI) {
System.out.print(c);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
},"t1").start();
new Thread(()->{
synchronized(o) {
for(char c: aC) {
System.out.print(c);
t2Started = true;
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
},"t2").start();
}
}
輸出結果:A1B2C3D4E5F6G7
③使用CountDownLatch限制t2先運行
import java.util.concurrent.CountDownLatch;
//使用CountDownLatch限制線程運行順序,以下是限制線程2先運行
public class Maureen_sync_wait_notify_02 {
private static CountDownLatch latch = new CountDownLatch(1);
public static void main(String[] args) {
final Object o = new Object();
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o) {
for (char c : aI) {
System.out.print(c);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t1").start();
new Thread(() -> {
synchronized (o) {
for (char c : aC) {
System.out.print(c);
latch.countDown();
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t2").start();
}
}
/**
* 線程調用start()函數,並不意味着線程立即佔用CPU運行,而是進入CPU的等待隊列中,即進入Ready狀態。根據操作系統的調度,決定選擇哪個線程運行
*/
輸出結果:A1B2C3D4E5F6G7
線程狀態遷移圖: