zoukankan      html  css  js  c++  java
  • 线程之Semaphore基本内容

      

    Semaphore(信号量)是一个线程同步结构,用于在线程间传递信号,以避免出现信号丢失,或者像锁一样用于保护一个关键区域.
    Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如 数据库连接
    Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。
    Semaphore可以控制某个资源可被同时访问的个数,
    通过acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。
    其中在执行semaphore.acquire()方法时,阻塞时间不同,因此前五个数据在打印时顺序不同,但之后每次都只会释放一个接受一个,
    故此,之后都是按顺序来排列的。

    Semaphore 注意事项:
    1.它的原理就是AQS。 Semaphore只是使用AQS的一种简单例子。
    Semaphore借助了线程同步框架AQS。AQS的分析可以参考文章Java同步框架AbstractQueuedSynchronizer,
    2. Semaphore(int permits, boolean fair)公平与非公平:
    当一个线程 release 释放了一个许可后,fair 决定了正在等待的线程该由谁获取许可,
    如果是公平竞争则等待时间最长的线程(基本上是最早建的线程)获取,如果是非公平竞争则随机选择一个线程获取许可。不传 fair 的构造函数默认采用非公开竞争。
    fair这个参数则表示是否是公平的,即等待时间越久的线程越能优先获得许可访问的权限。
    3.需要注意的是,任何线程在获得许可之后,使用共享资源完毕都需要执行归还操作,否则会有线程一直在等待。
    // 阻塞函数,直到有可访问的线程,才继续运行
    semaphore.acquire();
    4.使用流程:
    Semaphore实现为一种基于计数的信号量,Semaphore管理着一组虚拟的许可集合,这种许可可以作为某种凭证,来管理资源,在一些资源有限的场景下很有实用性,比如数据库连接,应用可初始化一组数据库连接,然后通过使用Semaphore来管理获取连接的许可,任何线程想要获得一个连接必须首先获得一个许可,然后再凭这个许可获得一个连接,这个许可将持续到这个线程归还了连接。
    在使用上,任何一个线程都需要通过acquire来获得一个Semaphore许可,这个操作可能会阻塞线程直到成功获得一个许可,因为资源是有限的,所以许可也是有限的,没有获得资源就需要阻塞等待其他线程归还Semaphore,而归还Semaphore操作通过release方法来进行,release会唤醒一个等待在Semaphore上的一个线程来尝试获得许可。

    5.实现互斥锁功能:
    比如任何时刻只能有一个线程获得许可,那么设置Semaphore的数量为1,一个线程获得这个Semaphore之后,任何到来的通过acquire来尝试获得许可的线程都会被阻塞,直到这个持有Semaphore的线程调用了release方法来释放Semaphore。
     Semaphore s = new Semaphore(1)",也就是该信号量的初始permits是1,但是在此后每次调用release方法都会导致permits加一。 如果能限制permits最大值1,最小值0,那就是真正的Mutex了。

    6.信号量用于线程同步,互斥量用户保护资源的互斥访问。
    信号量与互斥量的区别:
    互斥量用于线程的互斥,信号线用于线程的同步。
    互斥量值只能为0/1,信号量值可以为非负整数。信号量可以实现多个同类资源的多线程互斥和同步。
    互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。
    7.信号量解决生产者-消费者问题?

    更多 Java 并发编程方面的文章,请参见文集《Java 并发编程》


     

     

    Lock

    Lock是一个抽象概念。使得只有一个线程可以访问某个资源,并且Lock是不能被其他线程共享的。

    Mutex

    全程 MUTual EXclusion。

    目的:保护共享资源。
    典型的例子就是买票:票是共享资源,现在有两个线程同时过来买票。如果你不用 Mutex 在线程里把票锁住,那么就可能出现“把同一张票卖给两个不同的人(线程)”的情况。

    Semaphore

    目的:调度线程:一些线程生产(increase)同时另一些线程消费(decrease),Semaphore 可以让生产和消费保持合乎逻辑的执行顺序。
    有的人用 Semaphore 也可以把上面例子中的票“保护"起来,以防止共享资源冲突,必须承认这是可行的,

    但是 Semaphore 不是让你用来做这个的;如果你要做这件事,请用 Mutex。

    一个最典型的使用 Semaphore 的场景:

    a 源自一个线程,b 源自另一个线程,计算 c = a + b 也是一个线程。显然,第三个线程必须等第一、二个线程执行完毕它才能执行。在这个时候,我们就需要调度线程了:让第一、二个线程执行完毕后,再执行第三个线程。

    Semephore类是java.util.concurrent包下处理并发的工具类,Semephore能够控制任务访问资源的数量,如果资源不够,则任务阻塞,等待其他资源的释放。
    比如一个停车场有三个车位,当车位空余数量小于3时,车辆可以进入停车场停车。如果停车场已经没有了空余车位,后面来的车就不能进入停车场,
    只能在停车场外等待,等其他车辆离开之后才能进入。

    Semephore类的主要方法
    公平锁和非公平锁:程序在执行并发任务的时候,拿到同步锁的任务执行代码,其他任务阻塞等待,一旦同步锁被释放,CPU会正在等待的任务分配资源,获取同步锁。
    在这里又两种策略,CPU默认从等待的任务中随机分配,这是非公平锁;
    公平锁是按照等待时间优先级来分配,等待的时间越久,先获取任务锁。其内部是一个同步列队实现的。

    Semaphore 的使用

    所在包:java.util.concurrent:

    信号量 Semaphore 可以控制同时访问某个资源的线程个数。

    • public Semaphore(int permits) 构造方法,设置许可的个数,默认为非公平锁
    • public Semaphore(int permits, boolean fair) 构造方法,设置许可的个数,可以设置为公平锁(即等待越久的线程优先获得 permits)或非公平锁
    • void acquire() 获得一个许可 permits,没有的话,线程就阻塞
    • void acquire(int arg) 获得多个许可 permits,没有的话,线程就阻塞
    • boolean tryAcquire() 获得一个许可 permits,没有的话就返回 false,线程不阻塞
    • boolean tryAcquire(int permits) 获得多个许可 permits,没有的话就返回 false,线程不阻塞
    • void release() 释放一个许可 permits,在释放之前,必须先获得许可 permits
    • void release(int permits) 释放多个许可 permits,在释放之前,必须先获得许可 permits
    • int availablePermits() 得到当前可用的许可数目

    acquire();获取许可,Semephore任务数加一
    release();释放许可,Semephore任务数减一
    还有几个方法如tryAcquire()尝试获取许可,返回boolean值,不阻塞。availablePermits()还剩几个任务许可,等等几个方法和Lock类的用法相似。

      

    示例:
    假设当前有 5 辆车,10 个司机,每个司机轮流用车。

        // 同时只能有 5 个线程访问某个资源 车
        private static Semaphore semaphore = new Semaphore(5);
    
        public static void main(String[] args) {
            // 10 个司机
            for (int i = 1; i <= 10; i++) {
                (new Driver(i)).start();
            }
        }
    
        static class Driver extends Thread {
            private int i;
            public Driver(int i) {
                this.i = i;
            }
            public void run() {
                try {
                    semaphore.acquire();
    
                    System.out.println("Driver " + i + " is using car");
                    Thread.sleep(1000);
                    System.out.println("Driver " + i + " return back car");
    
                    semaphore.release();
                } catch (InterruptedException e) {
                }
            }
        }
    }
    

    可能的输出如下:

    Driver 1 is using car
    Driver 6 is using car
    Driver 5 is using car
    Driver 2 is using car
    Driver 3 is using car
    Driver 3 return back car
    Driver 5 return back car
    Driver 4 is using car
    Driver 2 return back car
    Driver 1 return back car
    Driver 6 return back car
    Driver 9 is using car
    Driver 8 is using car
    Driver 7 is using car
    Driver 10 is using car
    Driver 4 return back car
    Driver 10 return back car
    Driver 8 return back car
    Driver 7 return back car
    Driver 9 return back

     

    Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

    Semaphore

    Semaphore 有两个构造函数,参数为许可的个数 permits 和是否公平竞争 fair。通过 acquire 方法能够获得的许可个数为 permits,如果超过了这个个数,就需要等待。当一个线程 release 释放了一个许可后,fair 决定了正在等待的线程该由谁获取许可,

    如果是公平竞争则等待时间最长的线程获取,如果是非公平竞争则随机选择一个线程获取许可。不传 fair 的构造函数默认采用非公开竞争。

    Semaphore(int permits)
    Semaphore(int permits, boolean fair)
    

    一个线程可以一次获取一个许可,也可以一次获取多个。 在 acquire 等待的过程中,如果线程被中断,acquire 会抛出中断异常,

    如果希望忽略中断继续等待可以调用 acquireUninterruptibly 方法。同时提供了 tryAcquire 方法尝试获取,获取失败返回 false,获取成功返回 true。

    tryAcquire 方法可以在获取不到时立即返回,也可以等待一段时间。

    需要注意的是,没有参数的 tryAcquire 方法在有许可可以获取的情况下,无论有没有线程在等待都能立即获取许可,即便是公平竞争也能立即获取。

    public void acquire()
    public void acquireUninterruptibly()
    public boolean tryAcquire()
    public boolean tryAcquire(long timeout, TimeUnit unit)
    public void release()
    
    public void acquire(int permits)
    public void acquireUninterruptibly(int permits)
    public boolean tryAcquire(int permits)
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
    public void release(int permits)
    

    使用示例

    如下的示例中,测试方法 test 创建了多个线程,每个线程启动后都调用 acquire 方法,然后延时 5s 模仿业务耗时,最后调用 release 方法释放许可。

    public class SemaphoreTest {
        private int threadNum;
        private Semaphore semaphore;
    
        public SemaphoreTest(int permits,int threadNum, boolean fair) {
            this.threadNum = threadNum;
            semaphore = new Semaphore(permits,fair);
        }
        
        private void println(String msg){
            SimpleDateFormat sdf = new SimpleDateFormat("[YYYY-MM-dd HH:mm:ss.SSS] ");
            System.out.println(sdf.format(new Date()) + msg);
        }
        
        public void test(){
            for(int i =  0; i < threadNum; i ++){
                new Thread(() -> {
                    try {
                        semaphore.acquire();
                        println(Thread.currentThread().getName() + " acquire");
                        Thread.sleep(5000);//模拟业务耗时
                        println(Thread.currentThread().getName() + " release");
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
    

    公平与非公平

    在上述的示例中,如果 fair 传的是 true,则各个线程公平竞争,即按照等待时间的长短决定谁先获取许可。

    以 9 个线程竞争 3 个许可为例,执行结果如下,首选是线程 0、1、2 获取了许可,5s 后线程 3、4、5 获取了许可,最后是线程 6、7、8 获取许可,

    顺序基本上与创建线程并启动的先后顺序一致,也与各个线程等待的时间基本相符。

    [2017-08-20 21:47:21.817] Thread-0 acquire
    [2017-08-20 21:47:21.817] Thread-2 acquire
    [2017-08-20 21:47:21.817] Thread-1 acquire
    [2017-08-20 21:47:26.830] Thread-1 release
    [2017-08-20 21:47:26.830] Thread-0 release
    [2017-08-20 21:47:26.830] Thread-4 acquire
    [2017-08-20 21:47:26.830] Thread-3 acquire
    [2017-08-20 21:47:26.831] Thread-2 release
    [2017-08-20 21:47:26.831] Thread-5 acquire
    [2017-08-20 21:47:31.831] Thread-4 release
    [2017-08-20 21:47:31.831] Thread-3 release
    [2017-08-20 21:47:31.831] Thread-6 acquire
    [2017-08-20 21:47:31.831] Thread-7 acquire
    [2017-08-20 21:47:31.832] Thread-5 release
    [2017-08-20 21:47:31.832] Thread-8 acquire
    [2017-08-20 21:47:36.831] Thread-6 release
    [2017-08-20 21:47:36.831] Thread-7 release
    [2017-08-20 21:47:36.832] Thread-8 release
    

    在上述的示例中,如果 fair 传的是 false,则各个线程非公平竞争,随机选取一个线程获取许可。

    以 9 个线程竞争 3 个许可为例,执行结果如下,首先是线程 0、1、3 获取了许可,5s 后线程 2、5、7 获取了许可,最后是线程 4、6、8 获取许可,

    与线程创建启动时间无关,也与线程等待时间无关。

    [2017-08-20 17:45:09.893] Thread-0 acquire
    [2017-08-20 17:45:09.893] Thread-3 acquire
    [2017-08-20 17:45:09.893] Thread-1 acquire
    [2017-08-20 17:45:14.895] Thread-3 release
    [2017-08-20 17:45:14.895] Thread-0 release
    [2017-08-20 17:45:14.895] Thread-5 acquire
    [2017-08-20 17:45:14.895] Thread-1 release
    [2017-08-20 17:45:14.896] Thread-7 acquire
    [2017-08-20 17:45:14.896] Thread-2 acquire
    [2017-08-20 17:45:19.895] Thread-5 release
    [2017-08-20 17:45:19.895] Thread-4 acquire
    [2017-08-20 17:45:19.896] Thread-7 release
    [2017-08-20 17:45:19.896] Thread-6 acquire
    [2017-08-20 17:45:19.896] Thread-2 release
    [2017-08-20 17:45:19.896] Thread-8 acquire
    [2017-08-20 17:45:24.895] Thread-4 release
    [2017-08-20 17:45:24.896] Thread-8 release
    [2017-08-20 17:45:24.896] Thread-6 release
     
       
     3.1、默认构造器
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
    

      fair这个参数则表示是否是公平的,即等待时间越久的线程越能优先获得许可访问的权限。

    3.2、重要方法

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    
    public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }
    
    public void release() {
        sync.releaseShared(1);
    }
    
    public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }
    

        acquire()用来获取一个许可,而acquire(int permits)则是用来获取permits个许可,如果暂时无许可可获取,则会一直等待,直到获得许可;

    release()表示释放许可,而release(int permits) 则是表示释放permits个许可,

    另外在调用这两个方法的时候需要注意,在释放许可之前,必须要先获得许可才行。

    除此之外,上面4个方法在被调用后都会出现阻塞,而如果我们想立即得到执行结果,则可以直接使用下面几个方法:

    • 尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false。
    • /**
       * 尝试获取一个许可,若获取成功,则立即
       * 返回true,若获取失败,则立即返回false
       */
      public boolean tryAcquire() {
          return sync.nonfairTryAcquireShared(1) >= 0;
      }
    • 尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
    /**
     * 尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,* 否则则立即返回false
     */
    public boolean tryAcquire(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
    • 尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
    /**
     * 尝试获取permits个许可,若获取成功,则立即返回true,若获取失* 败,则立即返回false
     */
    public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }
    • 尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
    /**
     * 尝试获取permits个许可,若在指定的时间内获取成功,则立即返回* true,否则则立即返回false
     */
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
            throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }

    3.3、其他方法

    • 返回此信号量中当前可用的许可证数
    public int availablePermits() {
        return sync.getPermits();
    }
    
    • 返回正在等待获取许可证的线程数
    public final int getQueueLength() {
        return sync.getQueueLength();
    }
    
    • 是否有线程正在等待获取许可证
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }
    
    • 减少reduction个许可证。是个protected方法
    protected void reducePermits(int reduction) {
        if (reduction < 0) throw new IllegalArgumentException();
        sync.reducePermits(reduction);
    }
    
    • 返回所有等待获取许可证的线程集合。是个protected方法
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
    

     ===============

    导入

    Semaphore实现为一种基于计数的信号量,Semaphore管理着一组虚拟的许可集合,这种许可可以作为某种凭证,来管理资源,在一些资源有限的场景下很有实用性,比如数据库连接,应用可初始化一组数据库连接,然后通过使用Semaphore来管理获取连接的许可,任何线程想要获得一个连接必须首先获得一个许可,然后再凭这个许可获得一个连接,这个许可将持续到这个线程归还了连接。

    在使用上,任何一个线程都需要通过acquire来获得一个Semaphore许可,这个操作可能会阻塞线程直到成功获得一个许可,因为资源是有限的,所以许可也是有限的,没有获得资源就需要阻塞等待其他线程归还Semaphore,而归还Semaphore操作通过release方法来进行,release会唤醒一个等待在Semaphore上的一个线程来尝试获得许可。

    如果想要达到一种互斥的效果,比如任何时刻只能有一个线程获得许可,那么就可以初始化Semaphore的数量为1,一个线程获得这个Semaphore之后,任何到来的通过acquire来尝试获得许可的线程都会被阻塞直到这个持有Semaphore的线程调用了release方法来释放Semaphore。

    在实现上,Semaphore借助了线程同步框架AQS,AQS的分析可以参考文章Java同步框架AbstractQueuedSynchronizer

    同样借助了AQS来实现的是java中的可重入锁的实现,同样你可以在文章Java可重入锁详解中找到java中可重入锁的分析总结文档。

    在这些文章中已经分析过如何通过AQS来实现锁的语义,本文将继续分析AQS的应用实例,

    Semaphore作为一种线程间同步机制是非常轻量级的方案,所以学习和掌握Semaphore是有必要的。

    信号量Semaphore

    Semaphore的实现借助了同步框架AQS,下面的图片展示了Semaphore的代码结构,Semaphore使用一个内部类Sync来实现,而Sync继承了AQS来实现,Sync有两个子类,分别对应着公平模式和非公平模式的Semaphore 

     
    Semaphore代码结构

    首先来看一下Sync的构造函数:

      Sync(int permits) {
                setState(permits);
        }

    参数即为需要管理的许可数量,Sync使用AQS提供的setState方法来初始化共享变量state,后续通过acquire和release来获取和规划许可。

    下面首先分析的是公平模式下的获取许可方法:

            protected int tryAcquireShared(int acquires) {
                for (;;) {
                    if (hasQueuedPredecessors())
                        return -1;
                    int available = getState();
                    int remaining = available - acquires;
                    if (remaining < 0 ||
                        compareAndSetState(available, remaining))
                        return remaining;
                }
            }
            
        public final boolean hasQueuedPredecessors() {
            // The correctness of this depends on head being initialized
            // before tail and on head.next being accurate if the current
            // thread is first in queue.
            Node t = tail; // Read fields in reverse initialization order
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
    

    hasQueuedPredecessors方法表示的是是否有线程在等待许可,如果已经有线程在等待了,那么直接返回-1代表获取许可失败,否则再去获取,获取许可就是通过compareAndSetState方法来更新state的值,下面来看一下非公平模式下的获取许可的方法:

            final int nonfairTryAcquireShared(int acquires) {
                for (;;) {
                    int available = getState();
                    int remaining = available - acquires;
                    if (remaining < 0 ||
                        compareAndSetState(available, remaining))
                        return remaining;
                }
            }
    
    

    非公平模式和公平模式的区别在于公平模式会考虑是否已经有线程在等待,而非公平模式会快速去竞争,不会考虑是否有线程在前面等待,

    关于多个线程是如何去竞争共享变量而获得锁语义的内容需要参考文章Java同步框架AbstractQueuedSynchronizer

    下面来分析一下上面分析的两个方法是如何被调用的,上文中提到,我们是通过使用acquire方法来获得一个许可的,下面是Semaphore的acquire方法:

    
        public void acquire() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }
    
        public final void acquireSharedInterruptibly(int arg)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            if (tryAcquireShared(arg) < 0)
                doAcquireSharedInterruptibly(arg);
        }
        
        
    

    acquire方法调用了AQS基类的acquireSharedInterruptibly方法,而acquireSharedInterruptibly方法调用了其子类的tryAcquireShared方法,对应了公平模式和非公平模式下的tryAcquireShared方法。上面分析了获取许可的方法acquire,下面再来分析一下归还许可的方法release:

        public void release() {
            sync.releaseShared(1);
        }
        
        public final boolean releaseShared(int arg) {
            if (tryReleaseShared(arg)) {
                doReleaseShared();
                return true;
            }
            return false;
        }    
    
            protected final boolean tryReleaseShared(int releases) {
                for (;;) {
                    int current = getState();
                    int next = current + releases;
                    if (next < current) // overflow
                        throw new Error("Maximum permit count exceeded");
                    if (compareAndSetState(current, next))
                        return true;
                }
            }
    

    获取许可是通过减少state来完成的,而归还许可则是通过增加state来完成的,AQS通过维护一个共享变量state来实现多线程同步。

    为了更好的理解Semaphore的工作原理,下面展示一个使用示例,先来看代码:

    class Resource {
    
        /**
         * The resource
         * @return r
         */
        public Object getResource() {
            return new Object();
        }
    }
    
    /**
     *  The Thread Pool
     */
    class Pool {
        private static final int MAX_AVAILABLE = 100;
        private final int availableSemaphore;
        private Semaphore available;
    
        public Pool() {
            this(MAX_AVAILABLE);
        }
    
        public Pool(int available) {
            this(available, false);
        }
    
        public Pool(int available, boolean fairMode) {
            this.availableSemaphore = available;
            this.available = new Semaphore(available, fairMode);
    
            items = new Resource[availableSemaphore];
    
            for (int i = 0; i < availableSemaphore; i ++) {
                items[i] = new Resource();
            }
    
            used = new boolean[availableSemaphore];
        }
    
        public int availableSemaphore() {
            return this.availableSemaphore;
        }
    
        public Object getItem() throws InterruptedException {
          available.acquire();
          return getNextAvailableItem();
        }
    
        public void putItem(Object x) {
          if (markAsUnused(x))
            available.release();
        }
    
        // Not a particularly efficient data structure; just for demo
        protected Object[] items;
        protected boolean[] used;
    
        private synchronized Object getNextAvailableItem() {
          for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (!used[i]) {
              used[i] = true;
               return items[i];
            }
          }
          return null; // not reached
        }
    
        private synchronized boolean markAsUnused(Object item) {
          for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (item == items[i]) {
               if (used[i]) {
                used[i] = false;
                return true;
               } else
                 return false;
            }
          }
          return false;
        }
    }
    
    

    Pool类使用了Semaphore来管理一组许可,获得许可的线程可以获得一个Resource,而Resource是什么可以自定义,比如是数据库连接池。

    调用Pool类的getItem可以获得一个许可,而调用putItem将许可归还给Pool,下面展示了Pool的简单使用示例:

    public class SemaphoreDemo {
    
        public static void main(String ... args) {
    
            Pool pool = new Pool();
            Object resource = null;
    
            try {
                Resource r = (Resource) pool.getItem();
    
                 resource = r.getResource();
    
                //do your biz
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
    
                pool.putItem(resource); //release here
            }
    
        }
    
    }
    

     需要注意的是,任何线程在获得许可之后,使用共享资源完毕都需要执行归还操作,否则会有线程一直在等待。

    本文的内容到此也就结束了,Semaphore只是使用AQS的一种简单例子,AQS的强大之处就在于,你仅仅需要继承他,然后使用它提供的api就可以实现任意复杂的线程同步方案,AQS为我们做了大部分的同步工作,所以本文可以当成是对使用AQS的一种简单介绍,你应当去分析一下AQS的实现细节,并且加以总结。

    可以这么说,理解了AQS,就理解了java中线程同步是如何实现的,线程同步是并发的核心内容,如何保证多个线程可以安全高效的访问共享数据,是并发需要首要考虑的问题,而AQS解决了这些问题,未来还会对AQS进行深入分析总结。

     
     
     
  • 相关阅读:
    Eclipse的安装
    为Eclipse绑定Tomcat
    CHIL-SQL-快速参考
    CHIL-SQL-FORMAT() 函数
    CHIL-SQL-NOW() 函数
    CHIL-SQL-ROUND() 函数
    CHIL-SQL-LEN() 函数
    CHIL-SQL-MID() 函数
    【PAT A1060】Are They Equal
    vector
  • 原文地址:https://www.cnblogs.com/awkflf11/p/12636839.html
Copyright © 2011-2022 走看看