多线程循环打印

场景:两个线程交替打印字母和数字,效果如下: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);
        }
    }
}
相关推荐
kobe_t2 分钟前
数据安全系列6:从SM4国密算法谈到Bouncy Castle
java
欧的曼6 分钟前
cygwin环境下php脚本异常中断后自动重启
开发语言·php
要做朋鱼燕13 分钟前
ARM CoreSight:多核SoC调试追踪架构解析
开发语言·笔记·职场和发展·嵌入式·嵌入式软件
從南走到北17 分钟前
JAVA露营基地预约户外露营预约下单系统小程序
java·开发语言·小程序
曹牧25 分钟前
Java:实现List的定长截取
java·开发语言·list
Lxinccode28 分钟前
python(42) : 监听本地文件夹上传到服务器指定目录
服务器·开发语言·python·文件上传服务器·监听文件上传服务器
水无痕simon44 分钟前
8 shiro的web整合
java
木头左1 小时前
Python实现ETF网格自动化交易集成动量阈值判断
开发语言·自动化
CodeCraft Studio1 小时前
全球知名的Java Web开发平台Vaadin上线慧都网
java·开发语言·前端·vaadin·java开发框架·java全栈开发·java ui 框架
静水流深-刘申1 小时前
算法继续刷起-2025年09月26日
开发语言·c++·算法