zoukankan      html  css  js  c++  java
  • 多线程-java.util.concurrent-Semaphore

    什么是Semaphore信号量:

      Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

    正常的锁(synchronized锁或Lock锁)在任何时刻都只允许一个任务访问一项资源,而Semaphore允许n个任务访问这个资源。

    API:

      Semaphore(int permits) 创建给定数量的许可证和不公平设置的信号量。
      Semaphore(int permits, boolean fair) 创建给定数量的许可和设置公平性的信号量。
      void acquire() 从这个信号量获取许可证,阻塞直到有一个信号量可用或者线程被打断为止。
      void acquire(int permits) 从这个信号量获取给定数量的许可证,阻塞直到有一个信号量可用或者线程被打断为止。
      void acquireUninterruptibly() 从这个信号量获取许可证,阻塞直到一个信号量可用位置,不响应中断。
      void tryAcquire() 从这个信号量获取一个许可证,只有在调用时一个信号量可用。
      void tryAcquire(int permits)
      void tryAcquire(int permits, long timeout, TimeUnit timeUnit)
      void tryAcquire(long timeout, TimeUnit timeUnit)
      void release() 释放许可证,将其返回给信号量。

      int availablePermits() :返回此信号量中当前可用的许可证数。
      int getQueueLength():返回正在等待获取许可证的线程数。  
      boolean hasQueuedThreads() :是否有线程正在等待获取许可证。
      void reducePermits(int reduction) :减少reduction个许可证。是个protected方法。
      Collection getQueuedThreads() :返回所有等待获取许可证的线程集合。是个protected方法。

    应用场景:

      Semaphore可以用作流量控制,特别是公共资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型

    任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,二数据库的连接数只有10个,这时我们必须控制只有10个线

    程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候就可以用Semaphore来做流量控制。

    示例:   

    package org.burning.sport.javase.thread.semaphore;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    public class SemaphoreTest {
        private static ExecutorService exec = Executors.newFixedThreadPool(30);
        private static ThreadPoolManager threadPool = ThreadPoolManager.getInstanceThreadPool();
        private static Semaphore semaphore = new Semaphore(10); //①
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
    
                exec.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            semaphore.acquire(); //访问许可  
                            System.out.println("执行任务中.....");
                            semaphore.release(); //访问完成,释放  
                            System.out.println("--------" + semaphore.availablePermits());
    
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
    
            exec.shutdown();
            threadPool.shutdown();
        }
    }

         ①处,定义一个有10个许可证的信号量。就是说,同一时间内,最多允许10个线程可以执行acquire()release()之间的代码

         ②处,获取一个许可证。如果使用acquire(int permits) 可以获取多个许可证。每个acquire()阻塞,直到拿到一个许可证

         ③处,释放一个许可证。如果使用release(int permits) 可以释放多个许可证

         再来看一下acquireUninterruptibly()方法的使用 。这个方法的作用是使,等待进入acquire()方法的线程,不能被打断。就是当你调用interrupt()方法的时候,无效。来看看示例代码:

    public class SemaphoreTest2 {
    
        public static void main(String[] args) throws Exception{
            Semaphore semaphore = new Semaphore(3);
            // 模拟5个工人干活
            Thread t1 = new Thread(new Worker(semaphore));
            Thread t2 = new Thread(new Worker(semaphore));
            Thread t3 = new Thread(new Worker(semaphore));
            Thread t4 = new Thread(new Worker(semaphore));
            Thread t5 = new Thread(new Worker(semaphore));
            Thread t6 = new Thread(new Worker(semaphore));
    
            t1.setName("A");
            t2.setName("B");
            t3.setName("C");
            t4.setName("D");
            t5.setName("E");
            t6.setName("F");
    
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
            t6.start();
    
            Thread.sleep(1000);
            //第二个工人被打断
            t2.interrupt();  //
        }
    }
    
    class Worker implements Runnable{
        private Semaphore semaphore;
    
        public Worker(Semaphore semaphore) {
            this.semaphore = semaphore;
        }
    
        void doWork() {
            System.out.println(Thread.currentThread().getName() + "准备领干活的工具!!!");
            if(!semaphore.tryAcquire()) {
                System.out.println(Thread.currentThread().getName() + "没有领到干活的工具...不干了....");
            }
            System.out.println(Thread.currentThread().getName() + "领到了干活的工具....开始干活了!!!" + System.currentTimeMillis());
            try {
    //            semaphore.acquire();  // 不能被打断
                semaphore.acquireUninterruptibly(); ////模拟工人干活
                Thread.sleep((int) (Math.random() * 1000));
                System.out.println(Thread.currentThread().getName() + "活干完了,下班了!!!" + System.currentTimeMillis());
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "干活被别人打断了..." + e);
            }
    
        }
    
        @Override
        public void run() {
            doWork();
        }
    }

         我们看到的地方调用了interrupt()方法,如果把注释放开,就会被catch()代码块捕捉到。但是用的代码,就不会进入catch()代码块。

    参考:

      【1】《Think In Java》4th

      【2】《Java并发编程艺术》,方腾飞

      【3】《Java》高并发程序设计,葛一鸣

          【4】《Java并发编程核心方法与框架》,高洪岩

          【5】简书,https://www.jianshu.com/p/4c8170f0329e

  • 相关阅读:
    2
    spring jdbcTemplate 基本使用
    Day38-数据库入门学习
    DAY37-Python入门学习-进程池与线程池、协程、gevent模块
    Python入门学习-DAY36-GIL全局解释器锁、死锁现象与递归锁、信号量、Event事件、线程queue
    Python入门学习-DAY35-线程
    Python入门学习-DAY34-开启进程的两种方法、join方法、进程之间内存空间互相隔离、进程对象其他相关的属性或方法、僵尸进程与孤儿进程、守护进程、互斥锁
    Python入门学习-DAY33-基于UDP的套接字、socketserver模块、进程简单介绍
    Python入门学习-DAY32-链接循环与通信循环,粘包问题,远程控制,文件上传与下载
    Python入门学习-DAY31-三次握手原理与四次挥手,socket
  • 原文地址:https://www.cnblogs.com/happyflyingpig/p/10030028.html
Copyright © 2011-2022 走看看