要实现多线程轮流打印,那么线程需要交替执行。如果是两个线程可以通过wait和notify来进行交替,但是如果用多个线程来交替,简单的wait和notify就不能满足需求了。

        总数每加1,就要切换线程,首先线程启动后只要总数没有到1000,都是有机会去执行加1的,问题是线程需要判断自己该不该执行这个操作?

        我们知道HashMap底层有一个数组(这里不讨论HashMap的底层具体细节),假设HashMap不扩容,那么数组长度此时是固定的,put时会对key值进行Hash计算,让key值落在数组的某一个位置上,也就是说不论key值是多少,总会落在数组上。

        我们先来看一下最简单的Hash算法模数哈希(求余数),假设分子不固定,分母为3,那么余数只会有这些结果:0,1,2。也就是说余数是固定的,不管分子怎么变化,余数就会在这个范围内变化,如果分子是递增的,那么余数也是挨着轮询变化的,例如:

0%3=0, 1%3=1, 2%3=2, 3%3=0, 4%3=1, 5%3=2, 6%3=0
分子(递增):0,1,2,3,4,5,6
余数(轮询交替):0,1,2,0,1,2,0

        那对应到程序里面来,变化的是总数count,而且count是递增的,不变的是线程总数threadTotal,那么我们可以通过count%threadTotal得到一个余数,这个余数值刚好可以作为一个线程的标识,那么我们就用这个值作为线程的threadId。那么count每增加1,就轮询切换得到一个threadId,实现了线程轮流交替。

        那么线程怎么判断自己该不该执行加1操作呢,如果threadId == count % threadTotal为true,那么久可以确定线程有执行权利了。

        思路有了,直接上代码。补充:可以不用加notifyAll和wait,但是加了是为了提高效率,因为我们的目的是多线程交替执行,如果不加wait,那么线程判断自己不满足执行条件后下次循环还可能抢到锁,也就是说不满足条件的情况可能频繁抢到锁,就浪费cpu资源了。

/**
 * @author 蒋天文
 * @date 2023年03月16日 4:28:44 PM
 */
public class TestMultiThread {
 
    public static void main(String[] args) {
        Thread t1 = new Thread(new CalculateTask());
        Thread t2 = new Thread(new CalculateTask());
        Thread t3 = new Thread(new CalculateTask());
        Thread t4 = new Thread(new CalculateTask());
        t1.start();
        t2.start();
        t3.start();
        t4.start();
 
    }
}
 
class CalculateTask implements Runnable {
    private static volatile int count = 0;
 
    private static int threadTotal;
 
    private final int threadId;
 
    public CalculateTask() {
        this.threadId = threadTotal;
        threadTotal++;
    }
 
 
    @Override
    public void run() {
        while (count < 100) {
            synchronized (CalculateTask.class) {
                if (count % threadTotal == threadId) {
                    count++;
                    System.out.println("线程:" + threadId + ",对数加了1,结果为:" + count);
                    CalculateTask.class.notifyAll();
                } else {
                    try {
                        CalculateTask.class.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}