多线程打印ABC

这边我们使用的到的知识点是 线程加锁、等待、通知执行 

一、synchronized来实现

synchronized (lock) 给代码块加锁
lock.notifyAll(lock) 通知其他线程执行
lock.wait(lock)当前线程等待
    // notify, notifyAll 需要和synchronized联合使用    
    @Test
    public void method9() {
        // 创建阻塞队列
        ArrayBlockingQueue<Character> queue = new ArrayBlockingQueue(3);
        // 控制线程执行的次数
        CountDownLatch latch = new CountDownLatch(9);
        char[] chars = new char[] { 'A', 'B', 'C' };
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {

                try {
                    queue.put(chars[(i % 3)]);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();

        // 创建线程池三个线程
        ExecutorService pools = Executors.newFixedThreadPool(3);
        // 创建锁的对象,这种写法不支持分布式锁
        Object lock = new Object();

        for (int j = 0; j < 9; j++) {
            pools.execute(() -> {

                        synchronized (lock) {
                            try {
                                // 使用阻塞队列获取字符
                                Character char1 = queue.take();
                                latch.countDown();
                                System.out.println(String.valueOf(char1) + "  " + Thread.currentThread().getName());
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            try {

                                //通知其他线程执行
                                lock.notifyAll();
                                // 当前线程执行等待,下面代码不执行
                                lock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                    }
            );
        }

        try {
            //等待输出结果
            latch.await();
            //关闭线程池
            pools.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("执行结束");

    }

输出结果:

A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。 
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。


拓展学习:Java中Synchronized的用法_阳光日志-CSDN博客_synchronized

二、ReentrantLock 实现

   /**
     * 使用ReentrantLock 来控制并发
     */
    @Test
    public void method10() {

        // 创建阻塞队列
        ArrayBlockingQueue<Character> queue = new ArrayBlockingQueue(3);
        char[] chars = new char[] { 'A', 'B', 'C' };
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {

                try {
                    queue.put(chars[(i % 3)]);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();

        //锁对象
        ReentrantLock reen = new ReentrantLock();

        // 创建一个线程数是3的线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);
        // 线程计数控制
        CountDownLatch latch = new CountDownLatch(9);

        for (int i = 0; i < 9; i++) {

            pool.execute(() -> {
                // 加锁
                reen.lock();
                try {
                    Character out = queue.take();
                    System.out.println(Thread.currentThread().getName() + " " + out);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 释放锁
                reen.unlock();

                latch.countDown();
            });
        }

        try {
            // 程序等待线程执行完毕
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //关闭线程池
        pool.shutdown();
    }

执行结果:

三、使用Semaphore

    /**
     * 使用信号量来控制
     * Semaphore  Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。
     * 使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
     */
    @Test
    public void method4() {

        // 创建阻塞队列
        ArrayBlockingQueue<Character> queue = new ArrayBlockingQueue(3);
        char[] chars = new char[] { 'A', 'B', 'C' };
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {

                try {
                    queue.put(chars[(i % 3)]);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        ).start();

        Semaphore s1 = new Semaphore(1);

        CountDownLatch latch = new CountDownLatch(9);
        ExecutorService pool = Executors.newFixedThreadPool(5);

        for (int i = 0; i < 10; i++) {
            pool.execute(() -> {

                try {
                    //请求获得许可,如果有可获得的许可则继续往下执行,许可数减1。否则进入阻塞状态
                    s1.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                try {
                    Character out = queue.take();
                    System.out.println("线程" + Thread.currentThread().getName() + out);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();
                // 释放许可,许可数加1
                s1.release();
            });
        }

        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

执行结果

扩展资料:理解Semaphore及其用法详解_浩子的博客-CSDN博客_semaphore

线程同步机制有哪些?
1、互斥:互斥机制,保证同一时间,只有一个线程可以操作共享资源。如synchronized,lock
2、临界值:让多线程串行话访问资源
3、事件通知:通过事件的通知去保证大家都有序的访问共享资源
4、信号量:多个任务同时访问,同时限制数量。如发令枪CDL,Semaphore

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值