并发编程之 Semaphore信号量

Semaphore

信号量。允许多个线程同时访问。信号量为多线程写作提供了更加强大的控制方法。从广义上将,信号量是对锁的扩展。无论内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问同一个资源,而信号量却可以指定多个线程,同时访问某一资源。信号量主要提供了一下构造方法:

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

在构造对象的时候,可以指定信号量的准入数,即同时能申请多个许可。当每一个线程每次只申请一个许可时,这就相当于指定多少个线程可以访问同一资源了。

public class Semaphore implements java.io.Serializable {
    public Semaphore(int permits) {}
    public Semaphore(int permits, boolean fair) {}
    
    public void acquire() throws InterruptedException {}
    public void acquire(int permits) throws InterruptedException {}
    
    public void acquireUninterruptibly() {}
    public void acquireUninterruptibly(int permits) {}
    
    public boolean tryAcquire() {}
    public boolean tryAcquire(int permits) {}
    
    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {}
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {}
    
    public void release() {}
    public void release(int permits) {}
}

上面是Semaphore中比较重要的几个方法,大致通过方面就可以知道什么意思了。

  • acquire

    • acquire 获取一个许可。
    • acquire(n) 获取n个许可
  • acquireUninterruptibly

    • acquireUninterruptibly 获取一个许可,并且这个方法不可中断,这就意味着acquire方法是允许中断,通过方法声明也可以知道,一个抛出了InterruptedException,一个没有抛出异常。
    • acquireUninterruptibly(int permits) 获取多个许可,同样是不可中断的。
  • tryAcquire

    • tryAcquire 尝试获取一个许可,如果未获取到就直接返回false。获取到则返回true。
    • tryAcquire(int permits) 尝试获取多个许可,如果为未获取到就直接返回false,获取到则返回true。
  • tryAcquire with time

    • tryAcquire(long timeout, TimeUnit unit) 尝试获取一个许可,如果未获取到,则等待指定时间,如果还是未获取到则返回false,否则则返回true。
    • tryAcquire(int permits, long timeout, TimeUnit unit) 尝试获取多个许可,并等待指定之间。
  • release

    • release 释放一个许可信号
    • release(int permits) 释放多个许可信号

下面我们来通过例子演示一下通过获取信号量的方式来控制多个线程访问资源:

public class SemaphoreTest {
    private static final Semaphore semaphore = new Semaphore(10);
    public static class ICallable implements Callable<String> {

        @Override
        public String call() throws Exception {
            try {
                semaphore.acquire(2);
                System.out.println(System.currentTimeMillis() + "=>" + Thread.currentThread().getName() + " do.");
                TimeUnit.SECONDS.sleep(2);
            } finally {
                semaphore.release(2);
            }
            return null;
        }
    }
    public static void main(String[] args) {
        //创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        for (int i = 0; i < 20; i++) {
            executorService.submit(new ICallable());
        }

    }
}

结果:

1593945702515=>pool-1-thread-2 do.
1593945702515=>pool-1-thread-5 do.
1593945702515=>pool-1-thread-4 do.
1593945702515=>pool-1-thread-1 do.
1593945702515=>pool-1-thread-3 do.
1593945704517=>pool-1-thread-6 do.
1593945704517=>pool-1-thread-9 do.
1593945704517=>pool-1-thread-10 do.
1593945704517=>pool-1-thread-7 do.
1593945704517=>pool-1-thread-8 do.
1593945706517=>pool-1-thread-11 do.
1593945706517=>pool-1-thread-14 do.
1593945706517=>pool-1-thread-13 do.
1593945706517=>pool-1-thread-12 do.
1593945706517=>pool-1-thread-15 do.
1593945708518=>pool-1-thread-16 do.
1593945708518=>pool-1-thread-17 do.
1593945708518=>pool-1-thread-18 do.
1593945708518=>pool-1-thread-19 do.
1593945708518=>pool-1-thread-20 do.

通过结果可以看到,由于总数是10个信号量(许可),但是每次获取2个,所以结果中是以5个一组5个一组执行输出的。

当我们设置总许可为1时,就相当于Lock锁了。

public class SemaphoreTest2 {
    /** 自定义锁: 信号量为1个时,就相当于是锁 */
    static class SemaphoreLock {
        private static final Semaphore semaphore = new Semaphore(1);
        public void lock() throws InterruptedException {
            semaphore.acquire();
        }
        public void unlock(){
            semaphore.release();
        }
    }

    static class IRunnable implements Runnable{
        private static final SemaphoreLock lock = new SemaphoreLock();
        private static int num = 0;
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(10);
                lock.lock();
                num++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {

        List<Thread> threadList = new ArrayList<>();

        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread(new IRunnable());
            thread.start();
            threadList.add(thread);
        }

        for(Thread thread : threadList){
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(IRunnable.num);
    }
}

上面演示使用Semaphore实现一个自定义锁,比较简单,就不再赘述了。

你可能感兴趣的:(并发编程)