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();
}
}