多线程循环打印

场景:两个线程交替打印字母和数字,效果如下: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);
        }
    }
}
相关推荐
泡泡以安1 小时前
【Android逆向工程】第3章:Java 字节码与 Smali 语法基础
android·java·安卓逆向
毕设源码-朱学姐6 小时前
【开题答辩全过程】以 工厂能耗分析平台的设计与实现为例,包含答辩的问题和答案
java·vue.js
喵了meme7 小时前
C语言实战4
c语言·开发语言
码界奇点7 小时前
Python从0到100一站式学习路线图与实战指南
开发语言·python·学习·青少年编程·贴图
9ilk7 小时前
【C++】--- 特殊类设计
开发语言·c++·后端
sali-tec7 小时前
C# 基于halcon的视觉工作流-章68 深度学习-对象检测
开发语言·算法·计算机视觉·重构·c#
Spring AI学习8 小时前
Spring AI深度解析(9/50):可观测性与监控体系实战
java·人工智能·spring
java1234_小锋9 小时前
Spring IoC的实现机制是什么?
java·后端·spring
生骨大头菜9 小时前
使用python实现相似图片搜索功能,并接入springcloud
开发语言·python·spring cloud·微服务
绝不收费—免费看不了了联系我9 小时前
Fastapi的单进程响应问题 和 解决方法
开发语言·后端·python·fastapi