多线程问题
1.两线程交替打印a,b,共打印 100 次:
分析:
- 需要先打印出 a
- 需要保证交替打印
- 需要保证打印 100 次,不多不少
思路:使用一个 flag 变量进行通信, 当 flag 为奇数时打印 a,为偶数时打印 b。线程 t1 负责打印 a,flag 为奇数时它会await,并且使用 while 包裹防止虚假唤醒,当 flag 不是奇数时会 await,等待另一个线程打印完成后唤醒它。对 t2 线程也是一样。
注意:
- lock.unlock()需要写在 finally 里面保证一定释放
- await 一定在锁范围内,否则报illegalMoniterStateException,await 语义是放弃对锁的占有。
- 对打印条件while 判断,对打印次数while判断,但是在打印的时候需要再判断一次,双端判断
// 初版ReentrantLock 实现版本
public class AlternatePrint{
public static int cnt = 1;
static ReentrantLock lock = new ReentrantLock();
public static void main(){
Condition condition = lock.newCondition();
new Thread(()->{
while(cnt<=100){
try{
lock.lock();
while(cnt%2==1){
condition.await();
}
if(cnt<=100)System.out.println("a: "+Thread.currentThread().getName()+" -- "+cnt++);
condition.signal();
} catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
},"t1").start();
new Thread(()->{
while(cnt<=100){
try{
lock.lock();
while(cnt%2==0){
condition.await();
}
if(cnt<=100)System.out.println("b: "+Thread.currentThread().getName()+" -- "+cnt++);
condition.signal();
} catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
},"t2").start();
}
}
存在问题:两个线程间还是有很多代码是重复的,我需要把重复代码抽象一下;并且再尝试使用 semaphore 和 lockSupport 去实现
// 优化版本condition
public class AlternatePrint{
public static void main(){
PrintChar pc = new PrintChar();
new Thread(()->{
pc.doPrint('a',1);
},"t1").start();
new Thread(()->{
pc.doPrint('b',0);
},"t2").start();
}
static class PrintChar{
public int cnt = 1;
public ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void doPrint(char c,int flag){
while(cnt<=100){
lock.lock();
try{
while(cnt%2==flag){condition.await();}
if(cnt<=100)System.out.println(c+" : "+Thread.currentThread().getName()+" -- "+cnt++);
condition.signal();
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}// while cnt<=100
}
}
}
// lockSupport版本
2.手写线程池
梳理流程:
- 创建线程池类
- 创建成员变量和含参构造
- 继承线程类实现线程内部类
- 添加线程池 execute 方法
public class MyThreadPool {
private int corePool;
private BlockingQueue<Runnable> taskQueue;
private WorkThread[] threads;
public MyThreadPool(int corePool){
this.corePool = corePool;
taskQueue = new LinkedBlockingQueue<>();
threads = new WorkThread[corePool];
for(int i=1;i<=corePool;i++){
threads[i] = new WorkThread("thread--"+i);
threads[i].start();
}
}
class WorkThread extends Thread{
public WorkThread(String name){
super(name);
}
@Override
public void run(){
Runnable task;
while (true){
synchronized(taskQueue){
while (taskQueue.isEmpty()){
try {
taskQueue.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
task = taskQueue.poll();
}
if(task!=null)task.run();
}
}
}
public void execute(Runnable task){
synchronized(taskQueue){
taskQueue.add(task);
taskQueue.notify();
}
}
}
3.写锁降级
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 获取写锁
lock.writeLock().lock();
// 持有写锁尝试加读锁(可重入锁)
lock.readLock().lock();
// 释放写锁
lock.writeLock().unlock();
// 释放读锁
lock.readLock().unlock();
4.解决写线程饥饿问题
发展过程:读、写互斥 -> 读读共享,读写互斥 -> 乐观锁
static StampedLock stampedLock = new StampedLock();
public void read(){
long stamp = stampedLock.tryOptimisticRead();
System.out.println("reading");
if(!stampedLock.validate(stamp)){
System.out.println("被修改了,换用 read 锁重新读");
stamp = stampedLock.readLock();
System.out.println("redo reading!");
stampedLock.unlock(stamp);
}
}
5.CompletableFuture使用
基本不使用构造,使用两个静态方法完成
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, 2,
3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(),
Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
CompletableFuture.supplyAsync(()->{
return 1024;
},threadPoolExecutor).whenComplete((a,e)->{
System.out.println(a);
});
6.实现线程中断的三大方法
- volatile 实现:
private volatile boolean isStop; @Test public void volatileImpl() { new Thread(() -> { while (true) { System.out.println(Thread.currentThread().getName() + " is running!"); if (isStop) { System.out.println(Thread.currentThread().getName() + " interrupt!"); break; } } }, "t1").start(); try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();} new Thread(() -> { isStop = true; }, "t2").start(); }
- Atomic 类实现:
private AtomicBoolean atomicBoolean = new AtomicBoolean(false); @Test public void atomicImpl() { new Thread(() -> { while (true) { System.out.println(Thread.currentThread().getName() + " is running!"); if (atomicBoolean.get()) { System.out.println(Thread.currentThread().getName() + " interrupt!"); break; } } }, "t1").start(); try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();} new Thread(() -> { atomicBoolean.set(true); }, "t2").start(); }
- interrupt实现:
public void interruptImpl() { Thread t1 = new Thread(() -> { while (true) { System.out.println(Thread.currentThread().getName() + " is running!"); if (Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + " interrupt!"); break; } } }, "t1"); t1.start(); try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();} new Thread(() -> { t1.interrupt(); }, "t2").start(); }