目录

  • java多线程 线程交替执行(同步)的各种实现方案
  • 需求
  • 指定具体执行顺序
  • 实现一:wait_notify机制 + Thread标志位
  • 实现二:lock_condition机制 + Thread标志位
  • 实现三:semaphore信号量
  • 不指定具体执行顺序,只交替执行
  • 实现一:synchronized和wait、notify实现
  • 实现二:lock和condition实现
  • 总结


java多线程 线程交替执行(同步)的各种实现方案

需求

有两个线程,A线程内容是输出数字1到26,B线程内容是输出字母a-z,

要求A B 线程交替执行,最终输出如下图所示

java cron 同步时间_java

指定具体执行顺序

要求A B 线程交替执行,并且必须是A线程先执行,B后执行

解决思路:初始指定一个标志位,标记哪个线程可以运行,只要当前标志位不是当前线程(不满足运行条件),就一直等待

伪代码

线程A{
	while(满足当前线程的运行条件)
		不满足,则直接释放锁并进入wait状态
	执行当前线程内容
	通知下一个执行的线程,让其运行条件为true
}

实现一:wait_notify机制 + Thread标志位

实现思路:

  • 用标志位Thread标识哪个先执行
  • 线程A:只要标志位不是A就不满足运行条件,就等待;否则才执行内容,并切换Thread为下一个要执行的线程B
  • 线程A:只要标志位不是B就不满足运行条件,就等待;否则才执行内容,并切换Thread为下一个要执行的线程A

拓展:如果是三个线程交替,只需要让B线程执行完后把threadName标记为C,然后让C线程执行完后把threadName标记为A即可

public class AlternateThreads {

    private static final Object lock = new Object();
    private static String threadName = "A";

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                for (int i = 1; i <= 26; i++) {
                    //threadName只要不是线程A,就等待
                    while (threadName!="A") {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(i + " ");
                    threadName = "B";
                    lock.notify();
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (lock) {
                for (char c = 'a'; c <= 'z'; c++) {
                    //threadName只要不是线程A,就等待
                    while (threadName!="B") {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(c + " ");
                    threadName = "A";
                    lock.notify();
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}

实现二:lock_condition机制 + Thread标志位

实现思路:和wait_notiy实现的基本一致,也是,只不过wait变成了await,notify变成了signal

拓展:如果是三个线程交替,只需要让B线程执行完后把threadName标记为C,然后让C线程执行完后把threadName标记为A即可

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AlternateThreads {

    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    private static String threadName = "A";

    /**目标:交替执行的基础上,必须先执行A,再执行B
     * 方案:lock_condition机制 + Thread标志位
     * 实现思路:和我wait_notiy实现的基本一致,也是,只不过wait变成了await,notify变成了signal
     * @param args
     */
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            try {
                lock.lock();
                for (int i = 1; i <= 26; i++) {
                    while (threadName!="A") {
                        condition.await();
                    }
                    System.out.print(i + " ");
                    threadName = "B";
                    condition.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread threadB = new Thread(() -> {
            try {
                lock.lock();
                for (char c = 'a'; c <= 'z'; c++) {
                    while (threadName!="B") {
                        condition.await();
                    }
                    System.out.print(c + " ");
                    threadName = "A";
                    condition.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        threadA.start();
        threadB.start();
    }
}

实现三:semaphore信号量

实现思路:

  • 线程A:开始执行时,先申请信号量s1,执行结束后,再释放信号量s2,让B结束等待;
  • 线程B:开始执行时,先申请信号量s2,执行结束后,再释放信号量s1,让A结束等待;
  • 初始化:设置线程A对应的信号量初始为1,B线程对应信号量为0,保证A先执行

拓展:如果是三个线程交替,只需要让B线程执行完后release信号量C,然后让C线程执行完后release信号量A

public class AlternateThreads {

    private static Semaphore semaphoreA = new Semaphore(1);
    private static Semaphore semaphoreB = new Semaphore(0);

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 26; i++) {
                try {
                    semaphoreA.acquire();//申请信号量A
                    System.out.print(i + " ");
                    semaphoreB.release();//通知B可以运行了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadB = new Thread(() -> {
            for (char c = 'a'; c <= 'z'; c++) {
                try {
                    semaphoreB.acquire();//申请信号量B
                    System.out.print(c + " ");
                    semaphoreA.release();//通知A可以运行了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}

不指定具体执行顺序,只交替执行

要求:如果不要求A和B哪个先执行,只要求A和B交替执行

解决思路:保证互斥,直接执行内容,再notify唤醒其它线程,最后自己再释放锁进入wait状态

伪代码

线程A{
	while(是否满足当前线程A的运行条件?)
		不满足,则直接释放锁并进入wait
	执行当前线程内容
	通知下一个执行的线程B,让其运行条件为true
}

为什么这样不能保证哪个先执行?

因为各个线程只要竞争到锁,就会直接运行,后续再wait,而不是先判断是否要wait

实现一:synchronized和wait、notify实现

public class ThreadSyn {
    //实现方式一,synchronized和wait、notify实现,不保证先执行哪一个
    public static void test1() {
        Object lock = new Object();
        boolean flag = false;
        Thread a = new Thread(() -> {
            synchronized (lock) {
                for (int i = 1; i <= 26; i++) {
                    //直接运行
                    System.out.print(i + " ");
                    try {
                        //先唤醒,再阻塞
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.notify();//结束后唤醒,防止死锁
            }
        });
        Thread b = new Thread(() -> {
            synchronized (lock) {
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    //直接运行
                    System.out.print(ch + " ");
                    try {
                        //先唤醒,再阻塞
                        lock.notify();
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                lock.notify();//结束后唤醒,防止死锁
            }
        });
        a.start();
        b.start();
    }

实现二:lock和condition实现

//实现方式二:lock和condition实现,A B交替执行,也不确定先执行的是哪个,只能确保轮流执行
    public static void test2() {
        Lock lock = new ReentrantLock();
        Condition condition_A = lock.newCondition();
        Condition condition_B = lock.newCondition();
        Thread a = new Thread(() -> {
            lock.lock();
            for (int i = 1; i <= 26; i++) {
                //直接运行
                System.out.print(i + " ");
                try {
                    //先唤醒,再阻塞
                    condition_B.signal();//唤醒线程B
                    condition_A.await();//等待条件A
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            condition_B.signal();//执行完for循环后需释放最后在等待的B线程,防止死锁
            lock.unlock();

        });
        Thread b = new Thread(() -> {
            lock.lock();
            for (char ch = 'a'; ch <= 'z'; ch++) {
                System.out.print(ch + " ");//执行线程操作
                try {
                    condition_A.signal();//唤醒线程A
                    condition_B.await();//等待条件B
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            condition_A.signal();//执行完for循环后需释放最后在等待的A线程,防止死锁
            lock.unlock();
        });

        a.start();
        b.start();
    }

总结

如果想要指定执行顺序,则需要用信号量,或者自己实现一个标志位去模拟信号量

如果不需要指定执行顺序,则谁先竞争到锁就谁先执行,执行完后唤醒另一个线程,再进入wait状态