多线程交替打印奇偶数

多线程交替打印奇偶数

LockSupport

原理

image.png

贴一个之前的笔记,字迹比较潦草zz 有空的时候这里会补上说明

实现1

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

class Main{
    static Thread a = null, b=null;

    public static void main(String[] args){
        a = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 1;i<=99;i+=2){
                    System.out.println(i);
                    LockSupport.unpark(b);
                    LockSupport.park();
                }
            }
        });

        b = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 2;i<=100;i+=2){
                    LockSupport.park();
                    System.out.println(i);
                    LockSupport.unpark(a);
                }
            }
        });

        a.start();
        b.start();
    }
}

实现2

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Main {

    static AtomicInteger num = new AtomicInteger(1);

    public static void main(String[] args) throws InterruptedException {

        Print p1 = new Print();
        Print p2 = new Print();
        Thread t1 = new Thread(p1); //t1对应p1任务 但p1任务的线程挂在t2上方便t1唤醒它
        Thread t2 = new Thread(p2);
        t1.setName("thread-cc-1");
        t2.setName("thread-cc-2");

        p1.setT(t2);//给p1的Thread设置为t2方便t1唤醒它
        p2.setT(t1);
        t1.start();
        t2.start();

        // 唤醒线程t1打印奇数,线程1打印奇数,线程2打印偶数
        LockSupport.unpark(t1);

    }

    static class Print implements Runnable {
        private volatile Thread t;

        @Override
        public void run() {
            while (true) {
                // 进入之后立即阻塞
                LockSupport.park();
                if (num.get() > 100) {
                    LockSupport.unpark(t);
                    return;
                }
                System.out.println(Thread.currentThread().getName() + " : " + num.getAndIncrement());
		// 这里的t实际上是与currentThread不同的另一个线程
		// 因此实现了 奇数唤醒偶数线程,偶数唤醒奇数线程
                LockSupport.unpark(t);
            }
        }

        public void setT(Thread t) {
            this.t = t;
        }
    }
}

点评:可拓展性实现2相对较差一些(实现3个线程交替打印etc)

Synchronized + wait/notify实现

public class Main{
    public static void main(String[] args){
        MyPrint print = new MyPrint();
        Thread thread1 = new Thread(print,"A");
        Thread thread2 = new Thread(print,"B");
        thread1.start();
        thread2.start();

    }
}


class MyPrint implements Runnable{
    int i = 1;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                this.notify();//在这里唤醒的目的是为保证拿到锁的线程只有一个
                // 不会立即释放锁 退出代码块才会释放锁
                if (i <= 100){
                    System.out.println(Thread.currentThread().getName()+" "+i++);
                }else {
                    return;
                }
                try {
                    this.wait();//打印过数据的线程等待 必须等到没打印过数字的拿到锁了才能唤醒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Lock搭配Condition实现

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

public class Main {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        // 使用ReentrantLock的newCondition()方法创建三个Condition
        // 分别对应A、B、C三个线程
        Condition conditionA = lock.newCondition();
        Condition conditionB = lock.newCondition();

        // A线程
        new Thread(() -> {
            try {
                lock.lock();
                for (int i = 1; i <= 99; i+=2) {
                    System.out.println(Thread.currentThread().getName() +" " + i);
                    // 叫醒B线程
                    conditionB.signal();
                    // 本线程阻塞
                    conditionA.await();
                }
                // 这里有个坑,要记得在循环之后调用signal(),否则线程可能会一直处于
                // wait状态,导致程序无法结束
                conditionB.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 在finally代码块调用unlock方法
                lock.unlock();
            }
        }, "A").start();
        // B线程
        new Thread(() -> {
            try {
                lock.lock();
                for (int i = 2; i <= 100; i+=2) {
                    System.out.println(Thread.currentThread().getName() +" " + i);
                    conditionA.signal();
                    conditionB.await();
                }
                conditionA.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "B").start();
    }
}

Semaphore

import java.util.concurrent.Semaphore;

public class Main {

    public static void main(String[] args) {
        // 初始化许可数为1,A线程可以先执行
        Semaphore semaphoreA  = new Semaphore(1);
        // 初始化许可数为0,B线程阻塞
        Semaphore semaphoreB  = new Semaphore(0);

        new Thread(() -> {
            for (int i = 1; i <= 99; i+=2) {
                try {
                    // A线程获得许可,同时semaphoreA的许可数减为0,进入下一次循环时
                    // A线程会阻塞,知道其他线程执行semaphoreA.release();
                    semaphoreA.acquire();
                    // 打印当前线程名称
                    System.out.println(Thread.currentThread().getName() + " " + i);
                    // semaphoreB许可数加1
                    semaphoreB.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 2; i <= 100; i+= 2) {
                try {
                    semaphoreB.acquire();
                    System.out.println(Thread.currentThread().getName()+ " " + i);
                    semaphoreA.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}