Semaphore-信号灯机制

Semaphore-信号灯机制

[toc]

参考:
Semaphore-信号灯机制
Java多线程——Semaphore信号灯

总结:

Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数(即允许n个任务同时访问这个资源),例如,实现一个文件允许的并发访问数。

Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。

另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。

详情:

当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目,它是一个计数信号量,从概念上讲,信号量维护了一个许可集合,如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可,每个release() 添加一个许可,从而可能释放一个正在阻塞的获取者。

在线程池内创建线程并运行时,每个线程必须从信号量获取许可,从而保证可以使用该项。该线程结束后,线程返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。
信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。下面通过一个例子加以说明:

public class ThreadPoolTest
{
    public static void main(String[] args)
    {
        // create thread pool
        ExecutorService service = Executors.newCachedThreadPool();
        // semaphore 信号量 
        final Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++)
        {
            Runnable runnable = new Runnable()
            {
                public void run()
                {
                    try
                    {
                        semaphore.acquire();
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3 - semaphore.availablePermits()) + "个并发数");
                    try
                    {
                        Thread.sleep((long) (Math.random() * 10000));
                    }
                    catch (InterruptedException e1)
                    {
                        e1.printStackTrace();
                    }
                    System.out.println("线程" + Thread.currentThread().getName() + "即将离开");
                    semaphore.release();
                    // 下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
                    System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3 - semaphore.availablePermits()) + "个并发");
                };

            };
            service.execute(runnable);
        }

    }
}

该例子定义了一个newCachedThreadPool,在该Pool中利用for循环同时创建10个线程,现在通过Semaphore,创建一个只允许在线程池中有3个线程并发运行,sp.acquire()表示某个线程获得了一个信号灯,开始运行,在运行结束时,通过sp.release()还回这个信号灯,以便剩下的线程获得信号灯运行,sp.availablePermits()指的是当前信号灯库中有多少个可以被使用,由于例子中定义有3个信号灯,所以3-sp.availablePermits()就代表了当前有多少个线程在并发运行,上例运行结果如下:


Semaphore-信号灯机制_第1张图片
示例

Semaphore作为互斥锁使用:

当信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。

用信号量的方式(极端情况允许线程数1)实现的互斥锁,没有谁占有谁释放,这种通过限制线程数量的锁是线程执行完之后就释放,其他等待线程进入。

Semaphore的构造方法有两种:
第一种:

////用给定的许可数和非公平的公平设置创建一个 Semaphore。  
Semaphore(int permits) 

第一种构造方法创建的信号灯,在获取信号量的时候是随机的,没有一定的顺序。

第二种:

//用给定的许可数和给定的公平设置创建一个 Semaphore  
Semaphore(int permits, boolean fair) 

第二种构造方法可选地接受一个公平 参数。当设置为 false 时,此类不对线程获取许可的顺序做任何保证。特别地,闯入 是允许的,也就是说可以在已经等待的线程前为调用acquire() 的线程分配一个许可,从逻辑上说,就是新线程将自己置于等待线程队列的头部。当公平设置为 true 时,信号量保证对于任何调用acquire() 方法的线程而言,都按照处理它们调用这些方法的顺序(即先进先出;FIFO)来选择线程、获得许可。

通常,应该将用于控制资源访问的信号量初始化为公平的,以确保所有线程都可访问资源。为其他的种类的同步控制使用信号量时,非公平排序的吞吐量优势通常要比公平考虑更为重要。

你可能感兴趣的:(Semaphore-信号灯机制)