多线程循环打印

场景:两个线程交替打印字母和数字,效果如下:12ab34cd56ef......

synchronized

使用 synchronized 同步锁和 Object#wait() 和 Object#notifyAll(),在各个线程传入不同的 type 做区分,线程类型和当前打印类型不一致时则 wait。

java 复制代码
package com.example.demo;

public class Main {
    private static volatile int flag = 0; // 0 表示数字线程,1 表示字母线程
    private static int i = 1; // 数字计数器
    private static char c = 'a'; // 字母计数器

    public static void main(String[] args) {
        new Thread(new Print(0), "数字").start();
        new Thread(new Print(1), "字母").start();
    }

    static class Print implements Runnable {
        int type;

        public Print(int type) {
            this.type = type;
        }

        @Override
        public void run() {
            synchronized (Main.class) {
                while (true) {
                    if (flag != type) {
                        try {
                            Main.class.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }

                    if (flag == 0) {
                        System.out.println(Thread.currentThread().getName() + ": " + i++);
                        System.out.println(Thread.currentThread().getName() + ": " + i++);
                        flag = 1;
                    } else if (flag == 1) {
                        System.out.println(Thread.currentThread().getName() + ": " + c++);
                        System.out.println(Thread.currentThread().getName() + ": " + c++);
                        flag = 0;
                    }

                    Main.class.notifyAll(); // 每次操作后唤醒所有线程
                    if (i == 29 || c == 'z' + 1) {
                        break;
                    }
                }
            }
        }
    }
}

Lock

2.1 Lock 初级用法

注意:当线程调用 lock.lock() 时,如果锁已被其他线程持有,当前线程会进入阻塞状态(BLOCKED),不会消耗CPU资源

java 复制代码
package com.example.demo;

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

public class Main {
    private static Lock lock = new ReentrantLock();
    private static volatile int flag = 0;
    private static int i = 1; // 数字计数器
    private static char c = 'a'; // 字母计数器

    public static void main(String[] args) {
        new Thread(new Print(0), "数字").start();
        new Thread(new Print(1), "字母").start();
    }

    static class Print implements Runnable {
        int type;

        public Print(int type) {
            this.type = type;
        }

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    if (flag == type) {
                        if (flag == 0) {
                            System.out.println(Thread.currentThread().getName() + ": " + i++);
                            System.out.println(Thread.currentThread().getName() + ": " + i++);
                            flag = 1;
                        } else if (flag == 1) {
                            System.out.println(Thread.currentThread().getName() + ": " + c++);
                            System.out.println(Thread.currentThread().getName() + ": " + c++);
                            flag = 0;
                        }
                        if (i == 29 || c == 'z' + 1) {
                            break;
                        }
                    }
                } finally {
                    lock.unlock();
                }
            }
        }

    }
}

问题:

  1. 不必要的锁竞争 :线程每次循环都会尝试获取锁,即使当前 flag 不符合自己的类型;
  2. 公平性问题ReentrantLock 默认是非公平锁,可能导致线程饥饿(线程长期抢不到锁);

2.2 Lock 进阶用法

通过 Condition 类实现了类似方法一中线程等待的效果,避免了锁竞争和公平性问题。

java 复制代码
package com.example.demo;

import java.util.concurrent.locks.*;

public class Main {
    private static final Lock lock = new ReentrantLock();
    private static final Condition condition = lock.newCondition();
    private static int flag = 0;
    private static int i = 1;
    private static char c = 'a';

    public static void main(String[] args) {
        new Thread(() -> print(0, "数字"), "数字").start();
        new Thread(() -> print(1, "字母"), "字母").start();
    }

    private static void print(int type, String name) {
        while (true) {
            lock.lock();
            try {
                // 条件不满足时主动等待
                while (flag != type) {
                    condition.await(); // 释放锁并等待,避免竞争
                }
                
                if (type == 0) {
                    System.out.println(name + ": " + i++);
                    System.out.println(name + ": " + i++);
                    flag = 1;
                } else {
                    System.out.println(name + ": " + c++);
                    System.out.println(name + ": " + c++);
                    flag = 0;
                }

                if (i >= 29 || c > 'z') break;

                // 唤醒其他线程
                condition.signalAll();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
    }
}

此外还可以借助 Condition 类实现精准唤醒

java 复制代码
import java.util.concurrent.locks.*;

public class Main {
    private static final Lock lock = new ReentrantLock();
    private static final Condition c1 = lock.newCondition();
    private static final Condition c2 = lock.newCondition();
    private static int flag = 0;
    private static int i = 1;
    private static char c = 'a';

    public static void main(String[] args) {
        new Thread(() -> print(0, "数字", c1, c2), "数字").start();
        new Thread(() -> print(1, "字母", c2, c1), "字母").start();
    }

    private static void print(int type, String name, Condition cur, Condition next) {
        while (true) {
            lock.lock();
            try {
                // 条件不满足时主动等待
                while (flag != type) {
                    cur.await(); // 释放锁并等待,避免竞争
                }

                if (type == 0) {
                    System.out.println(name + ": " + i++);
                    System.out.println(name + ": " + i++);
                    flag = 1;
                } else {
                    System.out.println(name + ": " + c++);
                    System.out.println(name + ": " + c++);
                    flag = 0;
                }

                if (i >= 29 || c > 'z') break;

                // 精准唤醒下一个线程
                next.signal();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
    }
}

Semaphore

通过信号量控制

java 复制代码
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.*;

public class Main {
    private static final Lock lock = new ReentrantLock();
    private static Semaphore s1 = new Semaphore(1);
    private static Semaphore s2 = new Semaphore(0);
    private static int flag = 0;
    private static int i = 1;
    private static char c = 'a';

    public static void main(String[] args) {
        new Thread(() -> print(0, "数字", s1, s2), "数字").start();
        new Thread(() -> print(1, "字母", s2, s1), "字母").start();
    }

    private static void print(int type, String name, Semaphore cur, Semaphore next) {
        try {
            while (true) {
                // 如果当前线程对应的信号量是0,则阻塞
                cur.acquire();

                if (type == 0) {
                    System.out.println(name + ": " + i++);
                    System.out.println(name + ": " + i++);
                    flag = 1;
                } else {
                    System.out.println(name + ": " + c++);
                    System.out.println(name + ": " + c++);
                    flag = 0;
                }
                // 释放下一个线程的信号量
                next.release();
                if (i >= 29 || c > 'z') {
                    break;
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
相关推荐
264玫瑰资源库18 分钟前
问道数码兽 怀旧剧情回合手游源码搭建教程(反查重优化版)
java·开发语言·前端·游戏
pwzs28 分钟前
Java 中 String 转 Integer 的方法与底层原理详解
java·后端·基础
东阳马生架构30 分钟前
Nacos简介—2.Nacos的原理简介
java
普if加的帕43 分钟前
java Springboot使用扣子Coze实现实时音频对话智能客服
java·开发语言·人工智能·spring boot·实时音视频·智能客服
爱喝一杯白开水1 小时前
SpringMVC从入门到上手-全面讲解SpringMVC的使用.
java·spring·springmvc
王景程1 小时前
如何测试短信接口
java·服务器·前端
安冬的码畜日常1 小时前
【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)
开发语言·前端·人工智能·ai·扫雷游戏·ai辅助编程·辅助编程
朝阳5812 小时前
Rust项目GPG签名配置指南
开发语言·后端·rust
朝阳5812 小时前
Rust实现高性能目录扫描工具ll的技术解析
开发语言·后端·rust
程高兴2 小时前
基于Matlab的车牌识别系统
开发语言·matlab