package com.zillion.springcloudeurekaserver;
import org.junit.Test;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
/**
* 馬士兵公開課
* 一個面試題:
* 用兩個線程,一個輸出字母,一個輸出數字,交替輸出。1A2B3C4D5E6F7G
* https://ke.qq.com/course/399017?flowToken=1014847&taid=3803502778652329&dialog=1
*/
public class ThreadTest1 {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
static Thread t1 = null, t2 = null;
/**
* 運行結果1A2B3C4D5E6F7G
*/
@Test
public void testLockSupport() {
t1 = new Thread(new Thread(() -> {
for (char c : aI) {
System.out.print(c);
LockSupport.unpark(t2);
LockSupport.park();
}
}), "t1");
t2 = new Thread(new Thread(() -> {
for (char c : aC) {
LockSupport.park();
System.out.print(c);
LockSupport.unpark(t1);
}
}), "t2");
t1.start();
t2.start();
}
Object object = new Object();
/**
* 如果想用sychronized,必須是對某個對象加鎖,而不是包含的代碼
* notify:叫醒鎖定這個對象的等待隊列裏面的任意一個線程,notifyAll是叫醒所有
* wait:本來還在運行那。wait後就進入等待隊列了,同時釋放這把鎖
*/
@Test
public void testSyncWaitNofiy() {
new Thread(() -> {
synchronized (object) {
for (char c : aI) {
System.out.print(c);
try {
object.notify();
object.wait();//讓出鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
object.notify();//必須,否則無法終止程序
}
}, "t1").start();
new Thread(() -> {
synchronized (object) {
for (char c : aC) {
System.out.print(c);
try {
object.notify();
object.wait();//讓出鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
object.notify();//必須,否則無法終止程序
}
}, "t2").start();
}
private static volatile boolean t2Started = false;
/**
* 在testSyncWaitNofiy的基礎上,控制第一個線程先運行,
* 方法一,自旋
* 爲什麼調用了start() 。還不一定就是他先運行。因爲start()後,線程是進入cpu的等待裏面,準備好狀態。什麼時候調用是cpu裏面調度的事情了。
*/
@Test
public void testSyncWaitNofiy1() {
new Thread(() -> {
synchronized (object) {
while (!t2Started) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (char c : aI) {
System.out.print(c);
try {
object.notify();
object.wait();//讓出鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
object.notify();//必須,否則無法終止程序
}
}, "t1").start();
new Thread(() -> {
synchronized (object) {
for (char c : aC) {
System.out.print(c);
t2Started = true;
try {
object.notify();
object.wait();//讓出鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
object.notify();//必須,否則無法終止程序
}
}, "t2").start();
}
private static CountDownLatch latch = new CountDownLatch(1);
/**
* 在testSyncWaitNofiy的基礎上,控制第一個線程先運行,
* 方法二,CountDownLatch
* CountDownLatch(int count) //實例化一個倒計數器,count指定計數個數
* countDown() // 計數減一
* await() //等待,當計數減到0時,所有線程並行執行
*/
@Test
public void testSyncWaitNofiy2() {
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object) {
for (char c : aI) {
System.out.print(c);
try {
object.notify();
object.wait();//讓出鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
object.notify();//必須,否則無法終止程序
}
}, "t1").start();
new Thread(() -> {
synchronized (object) {
for (char c : aC) {
System.out.print(c);
latch.countDown();
try {
object.notify();
object.wait();//讓出鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
object.notify();//必須,否則無法終止程序
}
}, "t2").start();
}
enum ReadyToRun {T1, T2}
static volatile ReadyToRun r = ReadyToRun.T1;
/**
* 模擬自旋鎖,自旋就是原地打轉
* 沒輪到它的時候,不放棄cpu。自旋鎖有個好處是不經過操作系統
* 除了自旋鎖,擴展信息:
* 1.偏向鎖:只記錄線程id
* 2.自旋鎖:線程不放棄cpu。線程可以用wait放棄cpu,進入等待隊列裏面。應用場景。這段程序速度特別快。空轉幾遍就輪到他了。
* 3.重量級鎖:併發量大,線程多
* <p>
* 內核態,用戶態。正常情況下,申請一把鎖是從用戶態到內核態(操作系統內核)。效率偏低。
*/
@Test
public void testCas() {
new Thread(() -> {
for (char c : aI) {
while (r != ReadyToRun.T1) {
}
System.out.print(c);
r = ReadyToRun.T2;
}
}, "t1").start();
new Thread(() -> {
for (char c : aC) {
while (r != ReadyToRun.T2) {
}
System.out.print(c);
r = ReadyToRun.T1;
}
}, "t2").start();
}
static BlockingQueue<String> q1 = new ArrayBlockingQueue<>(1);//blockingQueue線程安全
static BlockingQueue<String> q2 = new ArrayBlockingQueue<>(1);
/**
* 開拓思路用
*/
@Test
public void testBlockingQueue() {
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();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(c);
try {
q2.put("ok");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2").start();
}
/**
* 管道流線程安全,read,write都是阻塞的
* 效率低
* 運行結果:1A2B3C4D5E6F7G1A2B3C4D5E16FA72GB3C4D5E6F7G
*/
public void testPipedStream() throws Exception {
//1.建立管道,管道連接
PipedInputStream input1 = new PipedInputStream();
PipedInputStream input2 = new PipedInputStream();
PipedOutputStream output1 = new PipedOutputStream();
PipedOutputStream output2 = new PipedOutputStream();
input1.connect(output1);
input2.connect(output2);
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();
}
/**
* 一把鎖。不同條件下面的鎖定。這個的優點是能準確通知哪個線程運行。
* 如果輪到自己。是從wait方法後面執行。
*/
@Test
public void testLockCondition() {
Lock lock = new ReentrantLock();
Condition conditionT1 = lock.newCondition();
Condition conditionT2 = lock.newCondition();
new Thread(() -> {
try {
lock.lock();
for (char c : aI) {
System.out.print(c);
conditionT2.signal();
conditionT1.await();
}
conditionT2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
try {
lock.lock();
for (char c : aC) {
System.out.print(c);
conditionT1.signal();
conditionT2.await();
}
conditionT1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2").start();
}
}