多线程循环打印

场景:两个线程交替打印字母和数字,效果如下: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);
        }
    }
}
相关推荐
虾球xz3 分钟前
CppCon 2015 学习:3D Face Tracking and Reconstruction using Modern C++
开发语言·c++·学习·3d
林鸿群6 分钟前
C#子线程更新主线程UI及委托回调使用示例
开发语言·c#
爱编程的喵11 分钟前
深入理解JavaScript原型机制:从Java到JS的面向对象编程之路
java·前端·javascript
on the way 12322 分钟前
行为型设计模式之Mediator(中介者)
java·设计模式·中介者模式
保持学习ing25 分钟前
Spring注解开发
java·深度学习·spring·框架
techzhi25 分钟前
SeaweedFS S3 Spring Boot Starter
java·spring boot·后端
异常君1 小时前
Spring 中的 FactoryBean 与 BeanFactory:核心概念深度解析
java·spring·面试
weixin_461259411 小时前
[C]C语言日志系统宏技巧解析
java·服务器·c语言
cacyiol_Z1 小时前
在SpringBoot中使用AWS SDK实现邮箱验证码服务
java·spring boot·spring