多线程案例

同时启动三个线程, 按顺序打印 ABC三个字符串.

要求: 三个线程: 第一个只能打印 A, 第二个只能打印 B, 第三个只能打印 C, 同时执行, 要求打印结果: ABC


public class SequencePrint {

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

    private static void print() {
        Thread t1 = new Thread(new Print("A", null));
        Thread t2 = new Thread(new Print("B", t1));
        Thread t3 = new Thread(new Print("C", t2));
        // 这里建立一个依赖关系
        // t2 依赖于 t1, 传入 t 后, 就会进入 if 语句, 等待 t1 执行完毕才会执行 t2, 打印 B
        // t3 依赖于 t2, 传入 t 后, 只有等 t2 执行完毕才会执行 t3, 也即只有 t1 和 t2 执行完毕, 才会打印 C
        // 这里这个依赖关系就直接导致了这三个线程传入后, 无论传入的顺序如何(这三个线程的系统轮转调度如何), 都会按顺序打印
        t2.start();
        t1.start();
        t3.start();
    }


    private static class Print implements Runnable{
        private String content;
        private Thread t;

        public Print(String content, Thread t) {
            this.content = content;
            this.t = t;
        }

        @Override
        public void run() {
            try {
                if (t != null) {
                    // 如果传入的 t 不为 null, 那就是 t2 和 t3 线程, 那就让他 join 等待前一个线程执行结束在执行
                    t.join();
                }
                // 如果不是 null, 那此时传入的就是 t1 线程, 直接打印即可
                System.out.println(content);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}

同时启动三个线程, 按顺序同时打印10次 A, B, C(进阶)

思路:
在这里插入图片描述

public class SequencePrint2 {
    public static void main(String[] args) {
        print2();
    }

    private static void print2() {
        Thread t1 = new Thread(new Print2("A"));
        Thread t2 = new Thread(new Print2("B"));
        Thread t3 = new Thread(new Print2("C"));
        t2.start();
        t1.start();
        t3.start();
    }

    private static class Print2 implements Runnable{
        private String content;
        // 创建两个和类绑定起来的属性: 字符串数组和下标
        private static String[] ARRAY = {"A", "B", "C"};
        private static int INDEX;

        public Print2(String content) {
            this.content = content;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    synchronized (ARRAY) {
                        while (!ARRAY[INDEX].equals(content)) {
                            // 这里要进行等待操作, wait 操作必须用在 synchronized 代码块里, 所以要进行加锁操作
                            // 这里每执行一次就得把锁释放掉, 重新判断是否等待, 所以 synchronized 加锁操作就得在 while外面
                            ARRAY.wait();
                        }
                        System.out.print(content);
                 
                        // 这里还需要考虑的输出是否规范, 也就是换行符的打印
                        // 逻辑就是: 如果下标指向的是 C 这个字符串, 那就打印换行符
                        if (INDEX == ARRAY.length - 1) {
                            System.out.println();
                        }

                        // 打印完以后, 怎么唤醒
                        // 1. 给下标加一, 更新下标
                        INDEX = (INDEX + 1) % ARRAY.length; // 这里类似于循环队列的操作
                        // 2. 唤醒线程
                        ARRAY.notifyAll();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

  1. 实现阻塞式队列
package Java05_26;

// 阻塞式队列
// (1) 基于数组的循环队列实现
// (2) 提供一个队列, 消费的时候(取出元素), 如果队列为空, 阻塞等待.
//     如果队列为满, 生产的时候(存元素), 阻塞等待

public class MyBlockingQueue<T> {

    private Object[] table;
    private int getIndex; // 取元素的索引
    private int putIndex; // 存元素的索引
    private int size; // 数组有效元素个数

    public MyBlockingQueue(int capacity) {
        table = new Object[capacity];
    }

    public synchronized void put(T element) throws InterruptedException {
        while (size == table.length) {
            // 如果队列满了, 就等待
            wait(); // this 对象绑定的 wait 方法
        }
        // 1. 把元素放到 putIndex 的位置
        table[putIndex] = element; // 存放元素
        // 2. 更新 putIndex 和 size 的值
        size++;
        putIndex = (putIndex + 1) % table.length;
        notifyAll();
        Thread.sleep(500);
    }

    public synchronized T get() throws InterruptedException {
        while (size == 0) {
            // 如果队列为空, 等待
            wait();
        }
        // 1. 把 getIndex 的元素取出
        Object element = table[getIndex]; // 取出元素
        // 2. 更新 getIndex 和 size 的值
        getIndex = (getIndex + 1) % table.length; // 注意这里是加1, 不是减1, 因为这相当于是一个循环队列
        size--;
        notifyAll();
        Thread.sleep(500);
        return (T)element;
    }

    // 这里必须使用 synchronized 加锁, 不能使用 volatile 不加锁, 如果不加锁, 会导致原子性丢失
    // 在 get/put 方法没有结束的时候, 就有可能获取到 size, 此时是没有原子性的
    public synchronized int getSize() {
        return size;
    }

    // 模拟使用自定义阻塞式队列
    public static void main(String[] args) {
        MyBlockingQueue<Integer> queue = new MyBlockingQueue<>(100);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    while (true) {
                        synchronized (queue) {
                            // 模拟生产面包
                            queue.put(1);
                            System.out.println("存放面包+1  " + queue.getSize());
                            // 这里直接打印出来的 size 值是不对的, 因为 put 方法和 getSize 方法都是加了锁的操作, 两个方法的实行时间间隔不能保证
                            // put 方法执行完后应该立刻打印此时的 size , 但是 getSize 操作还要竞争对象锁, 所以是没有原子性的, 所以会出现问题
                            // 解决方案: 加锁
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    while (true) {
                        synchronized (queue) {
                            // 模拟消费面包
                            Integer e = queue.get();
                            System.out.println("消费面包-1  " + queue.getSize());
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值