面试题:2个线程顺序输出:A1B2C3D4E5F6G7
代码地址:https://github.com/githubforliming/order.git
总结集中方法:
- park unpark
- synchronized wait notify
- Lock ReentrantLock
- 自旋
- BlockingQueue
- Semaphore*2
- TrasferQueue
一、先论控制线程启动顺序
package test;
import java.util.concurrent.CountDownLatch;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 前置知识:控制两个线程执行先后顺序 方式很多 我们用CountDownLatch(这个知识点之前文章写到过)
*/
public class Test00 {
public static void main(String[] args) throws InterruptedException {
// 让线程2先执行输出 再执行线程1的输出
CountDownLatch cdl = new CountDownLatch(1);
new Thread(()->{
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"输出");
},"线程1").start();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"输出");
cdl.countDown();
},"线程2").start();
}
}
输出结果:
线程2输出
线程1输出
二、park unpark
package test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.LockSupport;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式1: park unpark
* git地址:https://github.com/githubforliming/order.git
*/
public class Test01 {
public static void main(String[] args) throws InterruptedException {
// 定义数组
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
// 使用LockSupport LockSupport之前文章写过使用方式
public static void OrderTest(char[] nums, char[] letters) {
// 控制线程执行顺序
CountDownLatch cdl = new CountDownLatch(1);
// 存储线程
Map<String,Thread> container = new HashMap<>();
// 输出字母
Thread thread01 = new Thread(() -> {
for (int i = 0; i < letters.length; i++) {
// 输出字母
System.out.print(letters[i]);
cdl.countDown();
// 等线程2通知(unpark)
LockSupport.park();
// 通知线程2执行输出
LockSupport.unpark(container.get("Thread02"));
}
LockSupport.unpark(container.get("Thread02"));
});
// 输出数字
Thread thread02 = new Thread(() -> {
// 等线程1先执行输出 然后再执行线程2
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < nums.length; i++) {
// 输出数字
System.out.print(nums[i]);
//给线程1发券
LockSupport.unpark(container.get("Thread01"));
// 等线程2给发券
LockSupport.park();
}
LockSupport.unpark(container.get("Thread01"));
});
// 线程放到容器中
container.put("Thread01", thread01);
container.put("Thread02", thread02);
// 开启线程
thread01.start();
thread02.start();
}
}
// 输出结果:A1B2C3D4E5F6G7
三、 synchronized wait notify
package test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.LockSupport;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式2:synchronized wait notify
* 关于synchronized wait notify的使用 关注我公众号包括使用以及锁升级都有写到
* git地址:https://github.com/githubforliming/order.git
*/
public class Test02 {
public static void main(String[] args) throws InterruptedException {
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
// 使用wait notify 之前文章写过使用方式
public static void OrderTest(char[] nums, char[] letters) {
CountDownLatch cdl = new CountDownLatch(1);
// 锁
Object lock = new Object();
// 输出字母
Thread thread01 = new Thread(() -> {
// 争夺锁
synchronized (lock) {
for (int i = 0; i < letters.length; i++) {
// 输出字母
System.out.print(letters[i]);
cdl.countDown();
try {
// 通知另一个线程执行输出动作
lock.notify();
// 让出锁 等别人通知我再执行(notify)
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
lock.notify();
}
});
// 输出数字
Thread thread02 = new Thread(() -> {
// 争夺锁
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
for (int i = 0; i < nums.length; i++) {
// 输出
System.out.print(nums[i]);
try {
// 通知另一个线程执行输出动作
lock.notify();
// 让出锁 等别人通知我再执行(notify)
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
lock.notify();
}
});
// 开启线程
thread02.start();
thread01.start();
}
}
输出结果:A1B2C3D4E5F6G7
四、Lock ReentrantLock
package test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式3:Lock ReentrantLock
* 关于ReentrantLock Condition相关知识以及CAS 可以关注我公众号 我都有写到相关文章
* git地址:https://github.com/githubforliming/order.git
*/
public class Test03 {
public static void main(String[] args) throws InterruptedException {
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
// 使用wait notify 之前文章写过使用方式
public static void OrderTest(char[] nums, char[] letters) {
CountDownLatch cdl = new CountDownLatch(1);
// 锁 关于锁的使用
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 输出字母
Thread thread01 = new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < letters.length; i++) {
// 输出字母
System.out.print(letters[i]);
cdl.countDown();
try {
// 通知另一个线程执行输出动作
condition.signal();
// 让出锁 等别人通知我再执行(signal)
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 不要忘记unlock
lock.unlock();
}
});
// 输出数字
Thread thread02 = new Thread(() -> {
try {
// 等线程1先输出
cdl.await();
lock.lock();
for (int i = 0; i < nums.length; i++) {
// 输出字母
System.out.print(nums[i]);
try {
// 通知另一个线程执行输出动作
condition.signal();
// 让出锁 等别人通知我再执行(signal)
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 不要忘记unlock
lock.unlock();
}
});
// 开启线程
thread02.start();
thread01.start();
}
}
输出结果:A1B2C3D4E5F6G7
五、Lock ReentrantLock Condition 方式2
package test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式3:Lock ReentrantLock
* 关于ReentrantLock Condition相关知识以及CAS 可以关注我公众号 我都有写到相关文章
* 用2个condition就涉及condition原理了
* git地址:https://github.com/githubforliming/order.git
*/
public class Test04 {
public static void main(String[] args) throws InterruptedException {
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
// 使用wait notify 之前文章写过使用方式
public static void OrderTest(char[] nums, char[] letters) {
CountDownLatch cdl = new CountDownLatch(1);
// 锁 关于锁的使用
Lock lock = new ReentrantLock();
// 这个用法比第一个看着高级 因为每个线程有自己一个独立的Condition
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
// 输出字母
Thread thread01 = new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < letters.length; i++) {
// 输出字母
System.out.print(letters[i]);
cdl.countDown();
try {
// 通知另一个线程执行输出动作
condition2.signal();
// 让出锁 等别人通知我再执行(signal)
condition1.await();
} catch (Exception e) {
e.printStackTrace();
}
}
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 不要忘记unlock
lock.unlock();
}
});
// 输出数字
Thread thread02 = new Thread(() -> {
try {
// 等线程1先输出
cdl.await();
lock.lock();
for (int i = 0; i < nums.length; i++) {
// 输出字母
System.out.print(nums[i]);
try {
// 通知另一个线程执行输出动作
condition1.signal();
// 让出锁 等别人通知我再执行(signal)
condition2.await();
} catch (Exception e) {
e.printStackTrace();
}
}
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 不要忘记unlock
lock.unlock();
}
});
// 开启线程
thread02.start();
thread01.start();
}
}
输出结果:A1B2C3D4E5F6G7
六、自旋
package test;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式4:自旋 对于前几种来说 这种比较耗费CPU
*/
public class Test05 {
public static void main(String[] args){
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
static volatile WhichThread wt = WhichThread.T1;
public static void OrderTest(char[] nums, char[] letters){
// 输出字母
Thread thread01 = new Thread(() -> {
for (int i = 0; i < letters.length; i++) {
// 如果不是T1 就自旋
while (!WhichThread.T1.equals(wt));
// 输出
System.out.print(letters[i]);
// 设置为T2 让线程2输出
wt = WhichThread.T2;
}
});
// 输出数字
Thread thread02 = new Thread(() -> {
for (int i = 0; i < nums.length; i++) {
// 如果不是T2 就自旋
while (!WhichThread.T2.equals(wt));
// 输出
System.out.print(nums[i]);
// 设置为T1 让线程1输出
wt = WhichThread.T1;
}
});
// 开启线程
thread02.start();
thread01.start();
}
}
enum WhichThread{
T1,T2;
}
输出结果:A1B2C3D4E5F6G7
七、BlockingQueue
package test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式5:BlockingQueue
* https://github.com/githubforliming/order.git
*/
public class Test07 {
public static void main(String[] args){
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
// 使用wait notify 之前文章写过使用方式
public static void OrderTest(char[] nums, char[] letters){
BlockingQueue bqletters = new ArrayBlockingQueue(1);
BlockingQueue bqnums = new ArrayBlockingQueue(1);
// 输出字母
Thread thread01 = new Thread(() -> {
// 争夺锁 这里一定注意是先synchronized再执行for wait notify
for (int i = 0; i < letters.length; i++) {
try {
// 字母放入queue
bqletters.put(letters[i]);
// 阻塞获取数字 准备输出
Object take = bqnums.take();
System.out.print(take);
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 输出数字
Thread thread02 = new Thread(() -> {
for (int i = 0; i < nums.length; i++) {
try {
// 阻塞获取
Object take = bqletters.take();
// 输出
System.out.print(take);
// 数字放入queue
bqnums.put(nums[i]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 开启线程
thread02.start();
thread01.start();
}
}
输出结果:A1B2C3D4E5F6G7
八、Semaphore*2
package test;
import java.util.concurrent.Semaphore;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式6:Semaphore
* https://github.com/githubforliming/order.git
*/
public class Test08 {
public static void main(String[] args){
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
public static void OrderTest(char[] nums, char[] letters){
Semaphore semaphore01 = new Semaphore(0);
Semaphore semaphore02 = new Semaphore(0);
// 输出字母
Thread thread01 = new Thread(() -> {
try {
for (int i = 0; i < letters.length; i++) {
// 等待通知准备输出
semaphore01.acquire();
// 输出
System.out.print(letters[i]);
// 通知线程2输出
semaphore02.release();
}
} catch (Exception e) {
e.printStackTrace();
}
});
// 输出数字
Thread thread02 = new Thread(() -> {
try {
for (int i = 0; i < nums.length; i++) {
// 通知线程1输出
semaphore01.release();
// 等着通知准备输出
semaphore02.acquire();
// 输出
System.out.print(nums[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
});
// 开启线程
thread02.start();
thread01.start();
}
}
输出结果:A1B2C3D4E5F6G7
九、TrasferQueue
package test;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
/**
* @author 关注公众号:木子的昼夜编程
* 面试题:2个线程交替输出A1B2C3D4E5F6G7
* 方式6:TrasferQueue TrasferQueue之前没有讲到过 公众号以后文章会写
* https://github.com/githubforliming/order.git
*/
public class Test09 {
public static void main(String[] args){
char[] nums = {'1','2','3','4','5','6','7'};
char[] letters = {'A','B','C','D','E','F','G'};
OrderTest(nums, letters);
}
public static void OrderTest(char[] nums, char[] letters){
TransferQueue tq = new LinkedTransferQueue();
// 输出字母
Thread thread01 = new Thread(() -> {
try {
for (int i = 0; i < letters.length; i++) {
// 生产字母元素 阻塞等消费
tq.transfer(letters[i]);
// 阻塞等待其他线程生产元素
Object take = tq.take();
// 输出
System.out.print(take);
}
} catch (Exception e) {
e.printStackTrace();
}
});
// 输出数字
Thread thread02 = new Thread(() -> {
try {
for (int i = 0; i < nums.length; i++) {
// 阻塞等待其他线程生产元素
Object take = tq.take();
// 输出
System.out.print(take);
// 生产数字元素给消费者
tq.transfer(nums[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
});
// 开启线程
thread02.start();
thread01.start();
}
}
输出结果:A1B2C3D4E5F6G7
十、 唠唠
工作中可能有时候用不到一些技术,但是这些技术是很有趣的,喜欢编程的人可以自己写写这些demo可有意思了。
欢迎关注公众号:木子的昼夜编程