一个线程协同问题,15个经典面试问题

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
        final ReentrantLock lock = reentrantLock;
        lock.lock();
        try {
            condition.signal();
        } finally {
            lock.unlock();
        }
    });
    Thread b = new Thread(() -> {
        final ReentrantLock lock = reentrantLock;
        lock.lock();
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    a.start();
    b.start();
}

}

这个方案使用了Condition对象来实现object的notify、wait效果。当然,这个也有同样的问题。


# 正确解法1--基于错误解法2进行改进

我们看看,前面问题的根源在于,我们线程a,在去通知线程b的时候,有可能线程b还没开始wait,所以此时通知失效。

那么,我们是不是可以先等等,等线程b开始wait了,再去通知呢?
Thread a = new Thread(() -> {
        Global1.var++;
        final ReentrantLock lock = reentrantLock;
        lock.lock();
        try {
            // 1
            while (!reentrantLock.hasWaiters(condition)) {
                Thread.yield();
            }
            condition.signal();
        } finally {
            lock.unlock();
        }
    });
1处代码,就是这个思想,在signal之前,判断当前condition上是否有waiter线程,如果没有,就死循环;如果有,才去执行signal。

这个方法实测是可行的。


# 正确解法2

对正确解法1,换一个api,就变成了正确解法2.

Thread a = new Thread(() -> {
Global1.var++;
final ReentrantLock lock = reentrantLock;
lock.lock();
try {
// 1
while (reentrantLock.getWaitQueueLength(condition) == 0) {
Thread.yield();
}
condition.signal();
} finally {
lock.unlock();
}
});

1这里,获取condition上等待队列的长度,如果为0,说明没有等待者,则死循环。


# 正确解法3--基于Semaphore

刚开始,我们初始化一个信号量,state为0\. 线程b去获取信号量的时候,就会阻塞。

然后我们线程a再去释放一个信号量,此时线程b就可以继续执行。

public class Global1 {
public static volatile int var = 1;
public static final Semaphore semaphore = new Semaphore(0);

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
        semaphore.release();
    });
    a.setName("thread a");
    Thread b = new Thread(() -> {
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    b.setName("thread b");
    a.start();
    b.start();
}

}


# 正确解法4--基于CountDownLatch

public class Global1 {
public static volatile int var = 1;
public static final CountDownLatch countDownLatch = new CountDownLatch(1);

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
        countDownLatch.countDown();
    });
    a.setName("thread a");
    Thread b = new Thread(() -> {
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    b.setName("thread b");
    a.start();
    b.start();
}

}


# 正确解法5--基于BlockingQueue

这里使用了ArrayBlockingQueue,其他的阻塞队列也是可以的。

import countdown.CountdownTest;

public class Global1 {
public static volatile int var = 1;
public static final ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(1);

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
        arrayBlockingQueue.offer(new Object());
    });
    a.setName("thread a");
    Thread b = new Thread(() -> {
        try {
            arrayBlockingQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    b.setName("thread b");
    a.start();
    b.start();
}

}

# 正确解法6--基于FutureTask

我们也可以让线程b等待一个task的执行结果;而线程a在执行完修改var为2后,执行该任务,任务执行完成后,线程b就会被通知继续执行。

public class Global1 {
public static volatile int var = 1;
public static final FutureTask futureTask = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
System.out.println("callable task ");
return null;
}
});

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
        futureTask.run();
    });
    a.setName("thread a");
    Thread b = new Thread(() -> {
        try {
            futureTask.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    b.setName("thread b");
    a.start();
    b.start();
}

}

# 正确解法7--基于join

这个可能是最简洁直观的,哈哈。也是群里同学们提供的解法,真的有才!

public class Global1 {
public static volatile int var = 1;

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
    });
    a.setName("thread a");
    Thread b = new Thread(() -> {
        try {
            a.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    b.setName("thread b");
    a.start();
    b.start();
}

}


# 正确解法8--基于CompletableFuture

这个和第6种类似。都是基于future。

public class Global1 {
public static volatile int var = 1;
public static final CompletableFuture completableFuture =
new CompletableFuture();

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
        completableFuture.complete(new Object());
    });
    a.setName("thread a");
    Thread b = new Thread(() -> {
        try {
            completableFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    b.setName("thread b");
    a.start();
    b.start();
}

}

# 非阻塞--正确解法9--忙等待

这种代码量也少,只要线程b在变量为1时,死循环就行了。

public class Global1 {
public static volatile int var = 1;

public static void main(String[] args) {
    Thread a = new Thread(() -> {
        Global1.var++;
    });
    a.setName("thread a");
    Thread b = new Thread(() -> {
        while (var == 1) {
            Thread.yield();
        }

        if (Global1.var == 2) {
            //do something;
            System.out.println(Thread.currentThread().getName() + " good job");
        }
    });
    b.setName("thread b");

面试题总结

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

其它面试题(springboot、mybatis、并发、java中高级面试总结等)

job");
}
});
b.setName(“thread b”);

面试题总结

CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】

其它面试题(springboot、mybatis、并发、java中高级面试总结等)

[外链图片转存中…(img-nbxxM1fK-1630933537230)]

[外链图片转存中…(img-YBqAw524-1630933537232)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值