多线程循环打印ABC

目录

1、使用AtomicInteger的原子自增

2、Synchronized + wait + notify

3、ReentrantLock + Condition

4、Semaphore


1、使用AtomicInteger的原子自增

实现:三个线程循环打印ABC 1000次。

缺点:线程的run方法中有while循环,三个线程会频繁地进行线程切换,可能造成巨大的资源浪费。极端情况下,也有可能出现饥饿线程的场景。

public class MyThread implements Runnable{
    private String out;
    private AtomicInteger atomicInteger;

    private static String[] chars = {"A", "B", "C"};

    public MyThread(String out, AtomicInteger atomicInteger) {
        this.out = out;
        this.atomicInteger = atomicInteger;
    }

    @Override
    public void run() {
        // 循环打印ABC 1000次
        while (atomicInteger.get() < 3 * 1000) {
            if (out.equals(chars[atomicInteger.get() % 3])) {
                System.out.println(out);
                // 原子自增操作
                atomicInteger.getAndIncrement();
            }
        }
    }
}
public class Main {

    /**
     * 每个线程都处在while循环中,线程间切换可能造成巨大的资源浪费
     * 极端情况下也有可能出现饥饿线程的场景
     */
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger();
        Thread thread1 = new Thread(new MyThread("A", atomicInteger));
        Thread thread2 = new Thread(new MyThread("B", atomicInteger));
        Thread thread3 = new Thread(new MyThread("C", atomicInteger));

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

2、Synchronized + wait + notify

实现:三个线程循环打印ABC 1000次。

缺点:相比于AtomicInteger方法,while中的wait方法虽然能阻塞当前线程,减少线程间的切换导致的开销。但是lock.notifyAll方法后,是唤醒所有的阻塞线程,使三个线程重新竞争lock对象锁;竞争是公平的,所以也会有线程抢到锁后,重新进入阻塞状态,仍旧会有大量无用的线程切换。

public class MyThread1 implements Runnable{
    private String out;
    private static Integer status = 0;

    private static String[] chars = {"A", "B", "C"};
    private static final Object lock = new Object();

    public MyThread1(String out) {
        this.out = out;
    }

    @SneakyThrows
    @Override
    public void run() {
        // 打印A|B|C 1000次
        for (int index = 0; index < 1000; index++) {
            synchronized (lock) {
                while (!out.equals(chars[status % 3])) {
                    lock.wait();
                }
                System.out.println(Thread.currentThread().getName() + ":" + out);
                status++;
                lock.notifyAll();
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyThread1("A"), "线程1");
        Thread thread2 = new Thread(new MyThread1("B"), "线程2");
        Thread thread3 = new Thread(new MyThread1("C"), "线程3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

3、ReentrantLock + Condition

实现:三个线程循环打印ABC 1000次。

通过三个Condition分别控制各个线程的休眠与唤醒。condition.await()休眠线程,condition.signal()唤醒线程。

public class MyThread2 implements Runnable{
    private String out;
    private static Integer status = 0;

    private static String[] chars = {"A", "B", "C"};
    private ReentrantLock lock;
    private Condition current;
    private Condition next;

    public MyThread2(String out, ReentrantLock lock, Condition current, Condition next) {
        this.out = out;
        this.lock = lock;
        this.current = current;
        this.next = next;
    }

    @SneakyThrows
    @Override
    public void run() {
        // 打印A|B|C 1000次
        for (int index = 0; index < 1000; index++) {
            lock.lock();
            try {
                while (!out.equals(chars[status % 3])) {
                    current.await();
                }
                System.out.println(Thread.currentThread().getName() + ":" + out);
                status++;
                next.signal();
            } finally {
                lock.unlock();
            }
        }
    }
}
public class Main {
    @SneakyThrows
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition A = lock.newCondition();
        Condition B = lock.newCondition();
        Condition C = lock.newCondition();
        Thread thread1 = new Thread(new MyThread2("A", lock, A, B), "线程1");
        Thread thread2 = new Thread(new MyThread2("B", lock, B, C), "线程2");
        Thread thread3 = new Thread(new MyThread2("C", lock, C, A), "线程3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

4、Semaphore

实现:三个线程循环打印ABC 1000次。

与Condition方式有点类似,通过三个信号量控制三个线程的阻塞与唤醒,semaphore.acquire()获取许可,信号量-1,当信号量为0时,阻塞;semaphore.release()释放许可,信号量+1。

public class MyThread3 implements Runnable{
    private String out;
    private Semaphore current;
    private Semaphore next;

    public MyThread3(String out, Semaphore current, Semaphore next) {
        this.out = out;
        this.current = current;
        this.next = next;
    }

    @SneakyThrows
    @Override
    public void run() {
        // 打印A|B|C 1000次
        for (int index = 0; index < 1000; index++) {
            // 获取许可,current信号量-1
            current.acquire();
            System.out.println(Thread.currentThread().getName() + ":" + out);
            // 释放许可,next信号量+1
            next.release();
        }
    }
}
public class Main {
    @SneakyThrows
    public static void main(String[] args) {
        Semaphore A = new Semaphore(1);
        Semaphore B = new Semaphore(0);
        Semaphore C = new Semaphore(0);
        Thread thread1 = new Thread(new MyThread3("A", A, B), "线程1");
        Thread thread2 = new Thread(new MyThread3("B", B, C), "线程2");
        Thread thread3 = new Thread(new MyThread3("C", C, A), "线程3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

以上内容为个人学习理解,如有问题,欢迎在评论区指出。

部分内容截取自网络,如有侵权,联系作者删除。

你可能感兴趣的:(Java基础,面试,并发编程,java)