总结来源:
1. https://blog.csdn.net/XingXing_Java/article/details/91955441
2.
3.
... ...
1.交替打印奇偶数:
public class 交替打印奇偶数 {
static class SoulutionTask implements Runnable{
static int value = 0;
@Override
public void run() {
while (value <= 100){
synchronized (SoulutionTask.class){
System.out.println(Thread.currentThread().getName() + ":" + value++);
SoulutionTask.class.notify();
try {
SoulutionTask.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
new Thread(new SoulutionTask(), "偶数").start();
new Thread(new SoulutionTask(), "奇数").start();
}
}
思路分析:需要控制两个线程的执行顺序,偶线程执行完之后奇数线程执行,这个有点像通知机制,偶线程通知奇线程,奇线程再通知偶线程。而一看到通知/等待,立马就有朋友想到了Object中的wait和notify。没错,这里我们用wait和notify对其进行实现。
2.N个线程循环打印:
import java.util.concurrent.*;
public class N个线程循环打印{
static int result = 0;
public static void main(String[] args) throws InterruptedException {
int N = 3;
//N个线程
Thread[] threads = new Thread[N];
//信号量数组
final Semaphore[] syncObjects = new Semaphore[N];
//
for (int i = 0; i < N; i++) {
syncObjects[i] = new Semaphore(1);
if (i != N-1){
syncObjects[i].acquire();
}
}
//上一个线程持有下一个线程的信号量
for (int i = 0; i < N; i++) {
final Semaphore lastSemphore = i == 0 ? syncObjects[N - 1] : syncObjects[i - 1];
final Semaphore curSemphore = syncObjects[i];
final int index = i;
threads[i] = new Thread(new Runnable() {
public void run() {
try {
while (true) {
lastSemphore.acquire();
System.out.println("thread" + index + ": " + result++);
if (result > 100){
System.exit(0);
}
curSemphore.release();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
threads[i].start();
}
}
}
思路分析:
一般思路:
核心方法在run里面,可以看见和我们交替打印奇偶数原理差不多,这里将我们的notify改成了notifyAll,这里要注意一下很多人会将notifyAll理解成其他wait的线程全部都会执行,其实是错误的。这里只会将wait的线程解除当前wait状态,也叫作唤醒,由于我们这里用同步锁synchronized块包裹住,那么唤醒的线程会做会抢夺同步锁。
但是有一个问题是什么呢?当我们线程数很大的时候,由于我们不确定唤醒的线程到底是否是下一个要执行的就有可能会出现抢到了锁但不该自己执行,然后又进入wait的情况,比如现在有100个线程,现在是第一个线程在执行,他执行完之后需要第二个线程执行,但是第100个线程抢到了,发现不是自己然后又进入wait,然后第99个线程抢到了,发现不是自己然后又进入wait,然后第98,97…直到第3个线程都抢到了,最后才到第二个线程抢到同步锁,这里就会白白的多执行很多过程,虽然最后能完成目标。
还有其他用lock/condition也实现了这样的功能,还有老司机用比较新颖的方法比如队列去做,当然这里就不多提了,大致的原理都是基于上面的。
这里我说一下我的做法,在Java的多线程中提供了一些常用的同步器,在这个场景下比较适合于使用Semaphore,也就是信号量,我们上一个线程持有下一个线程的信号量,通过一个信号量数组将全部关联起来,代码如下:
import java.util.concurrent.*;
public class N个线程循环打印{
static int result = 0;
public static void main(String[] args) throws InterruptedException {
int N = 3;
//N个线程
Thread[] threads = new Thread[N];
//信号量数组
final Semaphore[] syncObjects = new Semaphore[N];
//
for (int i = 0; i < N; i++) {
syncObjects[i] = new Semaphore(1);
if (i != N-1){
syncObjects[i].acquire();
}
}
//上一个线程持有下一个线程的信号量
for (int i = 0; i < N; i++) {
final Semaphore lastSemphore = i == 0 ? syncObjects[N - 1] : syncObjects[i - 1];
final Semaphore curSemphore = syncObjects[i];
final int index = i;
threads[i] = new Thread(new Runnable() {
public void run() {
try {
while (true) {
lastSemphore.acquire();
System.out.println("thread" + index + ": " + result++);
if (result > 100){
System.exit(0);
}
curSemphore.release();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
threads[i].start();
}
}
}
通过这种方式,我们就不会有白白唤醒的线程,每一个线程都按照我们所约定的顺序去执行,这其实也是面试官所需要考的地方,让每个线程的执行都能再你手中得到控制,这也可以验证你多线程知识是否牢固。