线程(一):交替打印

线程(一):交替打印

1.线程同步的两个重要方法
方法名方法说明
notify()唤醒其它一个线程,但不释放锁
notifyAll()唤醒其它所有线程,但不释放锁
wait()休眠当前线程,释放锁

唤醒方法(notify()/notifyAll())只是让其它”休眠“的线程被唤醒,让线程有争夺锁的机会,本身并没有释放锁,只有执行到wait()时,才会释放线程,同时进入”休眠“(释放线程并进入休眠的原因是,保证释放锁以后不参与下一次争夺锁,让其它线程来抢,达到线程同步的目的)

2.线程通信
2.1 线程通信模板三原则
  1. 同步代码块外的循环控制次数;同步代码块内的循环控制行为(休眠/唤醒)
  2. 同步代码块内,循环内写休眠条件,循环下写线程任务与唤醒条件
  3. 每次唤醒都需要判断 次数 ,因为可能在当前线程休眠时已经满足打印次数了,唤醒以后无需再去争夺锁,而是结束线程
2.2 线程通信的两个案例
【例2.2.1】两个线程交替打印1~20

一个线程仅打印奇数,另一个线程仅打印偶数,两线程交替打印

public class PrintThread {
    // 线程同步的锁
    public static final Object LOCK = new Object();
    private static int count = 1;
    public static int border = 20;


    public static void main(String[] args) throws InterruptedException {
        // 打印奇数的线程
        new Thread(() -> {
            // 外层循环写次数
            while (count <= border) {
                synchronized (LOCK) {
                    // 内层循环写控制条件,不符合的就休眠,该线程打印奇数,因此当count为偶数时休眠
                    while (count % 2 == 0) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        // 唤醒以后做校验,不通过跳出内层循环
                        if (count > border) break;
                    }
                    // 跳出内层循环后跳出外层循环
                    if (count > border) break;
                    // 符合条件,则正常输出
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    LOCK.notifyAll();
                }
            }
        }, "奇数线程").start();

        // 打印偶数的线程
        new Thread(() -> {
            // 外层循环写次数
            while (count <= border) {
                synchronized (LOCK) {
                    // 内层循环写控制条件,不符合的就休眠,该线程打印偶数,因此当count为奇数时休眠
                    while (count % 2 == 1) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        // 唤醒以后做校验,不符合条件则跳出循环
                        if (count > border) break;
                    }
                    // 跳出内层循环后跳出外层循环
                    if (count > border) break;
                    // 符合条件,正常输出
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    LOCK.notifyAll();
                }
            }
        }, "偶数线程").start();
    }
}
【例2.2.2】三个线程轮流打印1~20

三个线程a/b/c,轮流打印从1~20,打印顺序如下:a打印1,b打印2,c打印3,a打印4,b打印5,···,如此循环,直到打印到20

public class PrintThread {
    // 线程同步的锁
    public static final Object LOCK = new Object();
    private static int count = 1;
    // 线程标记,初始情况下,让a线程第一个打印
    public static String flag = "a";
    public static int border = 20;


    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            // 外层循环写次数
            while (count <= border) {
                synchronized (LOCK) {
                     // 内层循环写控制条件,不符合的就休眠
                    while (!Thread.currentThread().getName().equals(flag)) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        // 唤醒以后做校验,不符合条件则跳出循环
                        if (count > border) break;
                    }
                    // 跳出内层循环后跳出外层循环
                    if (count > border) break;
                    // 符合条件,则正常输出
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    flag = "b";
                    LOCK.notifyAll();
                }
            }
            System.out.println(Thread.currentThread().getName() + " 线程任务运行完成 ");
        }, "a").start();
        new Thread(() -> {
            while (count <= border) {
                synchronized (LOCK) {
                    while (!Thread.currentThread().getName().equals(flag)) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (count > border) break;
                    }
                    if (count > border) break;
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    flag = "c";
                    LOCK.notifyAll();
                }
            }
            System.out.println(Thread.currentThread().getName() + " 线程任务运行完成 ");
        }, "b").start();
        new Thread(() -> {
            while (count <= border) {
                synchronized (LOCK) {
                    while (!Thread.currentThread().getName().equals(flag)) {
                        try {
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (count > border) break;
                    }
                    if (count > border) break;
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    flag = "a";
                    LOCK.notifyAll();
                }
            }
            System.out.println(Thread.currentThread().getName() + " 线程任务运行完成 ");
        }, "c").start();
    }
}
3. 可重入锁ReentrantLock实现三线程轮流打印

上面的wait()/notify()/notifyAll()方法虽然可以实现线程的同步,但是无法做到“点对点”通信。当执行"notify()"方法时,会随机唤醒等待池中的一个线程,不能指定唤醒某一个特定的线程,所以才需要引入一个“flag”用于标记。Condition接口解决了这个问题,它可以“点对点”指定,唤醒指定的线程

方法名方法说明
signal()唤醒指定线程,但不释放锁
await()休眠当前线程,释放锁
public class PrintThread {
    // 线程同步的锁
    private static int count = 1;
    // 线程标记,初始情况下,让a线程第一个打印
    public static String flag = "a";
    public static int border = 20;

    public static void main(String[] args) throws InterruptedException {
        // 创建可重入锁
        ReentrantLock lock = new ReentrantLock();
        // 分别创建线程a、b、c的condition
        Condition threadA = lock.newCondition();
        Condition threadB = lock.newCondition();
        Condition threadC = lock.newCondition();
        new Thread(() -> {
            // 外层循环控制次数
            while (count <= border) {
                lock.lock();
                try {
                    // 内层循环控制行为,不符合条件则进入等待
                    while (!Thread.currentThread().getName().equals(flag)) {
                        try {
                            threadA.await();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (count > border) break;
                    }
                    if (count > border) break;
                    // 符合条件,执行代码
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    flag = "b";
                    // 好处是不会唤醒所有线程,而是只唤醒特定的那个线程
                    threadB.signalAll();
                } finally {
                    lock.unlock();
                }
            }
            System.out.println(Thread.currentThread().getName() + " 线程任务运行完成 ");
        }, "a").start();
        new Thread(() -> {
            // 外层循环控制次数
            while (count <= border) {
                lock.lock();
                try {
                    // 内层循环控制行为,不符合条件则进入等待
                    while (!Thread.currentThread().getName().equals(flag)) {
                        try {
                            threadB.await();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (count > border) break;
                    }
                    if (count > border) break;
                    // 符合条件,执行代码
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    flag = "c";
                    // 好处是不会唤醒所有线程,而是只唤醒特定的那个线程
                    threadC.signal();
                } finally {
                    lock.unlock();
                }
            }
            System.out.println(Thread.currentThread().getName() + " 线程任务运行完成 ");
        }, "b").start();
        new Thread(() -> {
            // 外层循环控制次数
            while (count <= border) {
                lock.lock();
                try {
                    // 内层循环控制行为,不符合条件则进入等待
                    while (!Thread.currentThread().getName().equals(flag)) {
                        try {
                            threadC.await();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        if (count > border) break;
                    }
                    if (count > border) break;
                    // 符合条件,执行代码
                    System.out.println(Thread.currentThread().getName() + " 正在打印 " + count++);
                    flag = "a";
                    // 好处是不会唤醒所有线程,而是只唤醒特定的那个线程
                    threadA.signalAll();
                } finally {
                    lock.unlock();
                }
            }
            System.out.println(Thread.currentThread().getName() + " 线程任务运行完成 ");
        }, "c").start();
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值