• 多线程轮流打印


    一、背景

    面试的时候,有一个高频的笔试题:

    让2个线程轮流打印,a线程是打印ABCDEFGHIJ,b线程是打印1、2、3、4、5、6、7、8、9、10

    二、原理

    这种类型的面试题,主要是考察object的wait()方法和notify()方法的使用

    wait()方法用法:

    o.wait()会让正在o对象上活动的当前线程进入等待状态,并且释放之前占用的o对象的锁

    notify()方法用法:

    o.notify()唤醒正在o对象上等待的线程, 只会通知,不会释放之前占用的o对象的锁

    三、用法

    2个线程轮流打印,a线程是打印ABCDEFGHIJ,b线程是打印1、2、3、4、5、6、7、8、9、10

    1. package com.iflytek.sda;
    2. import java.util.Arrays;
    3. import java.util.List;
    4. public class ThreadTest {
    5. private static final Object lock = new Object();
    6. private static boolean ifPrint = false;
    7. public static void main(String[] args) {
    8. Thread ta = new Thread(() ->{
    9. // 要打印的list
    10. List<String> list = Arrays.asList("A","B","C","D","E","F","G","H","I","J");
    11. for (int i = 0; i < list.size(); i++){
    12. synchronized (lock) {
    13. // 如果ifPrint的值为true,就会进入等待状态
    14. while (ifPrint) {
    15. try{
    16. lock.wait();
    17. }catch (Exception e){
    18. }
    19. }
    20. // 到了这一步,说明ifPrint的值为false,然后就会打印
    21. System.out.print(list.get(i));
    22. // 修改ifPrint的值为true,这样再次进入该方法时会进入等待状态,不会进行打印
    23. ifPrint = true;
    24. // 唤醒正在lock对象上等待的线程
    25. lock.notify();
    26. }
    27. }
    28. });
    29. Thread tb = new Thread(() ->{
    30. for (int i = 0; i < 10; i++){
    31. synchronized (lock) {
    32. // 如果ifPrint的值为false,就会进入等待状态
    33. while (!ifPrint) {
    34. try{
    35. lock.wait();
    36. }catch (Exception e){
    37. }
    38. }
    39. // 到了这一步,说明ifPrint的值为true,然后就会打印
    40. System.out.print(i+1);
    41. // 修改ifPrint的值为false,这样再次进入该方法时会进入等待状态,不会进行打印
    42. ifPrint = false;
    43. // 唤醒正在lock对象上等待的线程
    44. lock.notify();
    45. }
    46. }
    47. });
    48. ta.start();
    49. tb.start();
    50. }
    51. }

    一开始,ifPrint为false,此时,ta线程会进行打印,打印A;tb线程会处于等待状态;

    等ta打印完毕,修改ifPrint为true,并且唤醒其它等待线程。

    此时,ta线程会处于等待状态;tb线程会进行打印,打印1

    等tb打印完毕,修改ifPrint为false,并且唤醒其它等待线程。

    此时,ta线程会进行打印,打印B;tb线程会处于等待状态;

    ....

    以此类推:

     四、升级

    上面是2个线程,可以通过true、false的两种状态来区分,那如果是3个线程呢,4个线程呢,要怎么办?

    3个线程,轮流打印,第一个线程只打印A,第二个线程只打印B,第三个线程只打印C

    1. package com.iflytek.sda;
    2. public class ThreadTest2 {
    3. private static Integer flag = 1;
    4. private static final Object lock = new Object();
    5. public static void main(String[] args) {
    6. Thread threadA = new Thread(()->{
    7. for (int i = 0 ; i < 10; i++){
    8. synchronized (lock){
    9. while(flag != 1){
    10. try{
    11. lock.wait();
    12. }catch (Exception e){
    13. }
    14. }
    15. System.out.print("A");
    16. flag = 2;
    17. lock.notifyAll();
    18. }
    19. }
    20. });
    21. Thread threadB = new Thread(()->{
    22. for (int i = 0 ; i < 10; i++){
    23. synchronized (lock){
    24. while(flag != 2){
    25. try{
    26. lock.wait();
    27. }catch (Exception e){
    28. }
    29. }
    30. System.out.print("B");
    31. flag = 3;
    32. lock.notifyAll();
    33. }
    34. }
    35. });
    36. Thread threadC = new Thread(()->{
    37. for (int i = 0 ; i < 10; i++){
    38. synchronized (lock){
    39. while(flag != 3){
    40. try{
    41. lock.wait();
    42. }catch (Exception e){
    43. }
    44. }
    45. System.out.print("C");
    46. flag = 1;
    47. lock.notifyAll();
    48. }
    49. }
    50. });
    51. threadA.start();
    52. threadB.start();
    53. threadC.start();
    54. }
    55. }

    这里的代码写在同一个类里面,标志位写在最上面可以共用,实际用法中都不在一个类里面,要怎么办呢?

    1. package com.example.demo.thread;
    2. public class ThreadPrint {
    3. public static Integer flag = 1;
    4. public static void main(String[] args) {
    5. Thread ta = new Thread(new ThreadA());
    6. Thread tb = new Thread(new ThreadB());
    7. ta.start();
    8. tb.start();
    9. }
    10. }
    11. class ThreadA implements Runnable{
    12. @Override
    13. public void run() {
    14. for(int i = 0 ; i < 10; i++){
    15. synchronized (ThreadPrint.class){
    16. while (ThreadPrint.flag != 1){
    17. try {
    18. ThreadPrint.class.wait();
    19. }catch (Exception e){
    20. }
    21. }
    22. System.out.print("1");
    23. ThreadPrint.flag = 2;
    24. ThreadPrint.class.notifyAll();
    25. }
    26. }
    27. }
    28. }
    29. class ThreadB implements Runnable{
    30. @Override
    31. public void run() {
    32. for(int i = 0 ; i < 10; i++){
    33. synchronized (ThreadPrint.class){
    34. while (ThreadPrint.flag != 2){
    35. try {
    36. ThreadPrint.class.wait();
    37. }catch (Exception e){
    38. }
    39. }
    40. System.out.print("A");
    41. ThreadPrint.flag = 1;
    42. ThreadPrint.class.notifyAll();
    43. }
    44. }
    45. }
    46. }

    好几个线程同理,

    另外,超过两个线程就需要使用notifyAll,唤醒对象上的所有线程

  • 相关阅读:
    springMVC拦截器
    【MATLAB源码-第53期】m代码基于粒子群算法(PSO)的三维路径规划,显示最优路径和适应度曲线。
    一种基于网络流量风险数据聚类的APT攻击溯源方法
    【Spring Boot】如何运用Spring Cache并设置缓存失效时间
    Oracle基本介绍与基本使用
    redis淘汰策略
    【Java面试】数据库连接池有什么用?它有哪些关键参数?
    如何使用高压放大器驱动高容性负载
    踩坑笔记: 基于 rust-analyzer 在 vscode 中进行 rust 开发配置问题
    【云原生之k8s】kubernetes核心组件
  • 原文地址:https://blog.csdn.net/CelineT/article/details/128114451