thread_Semaphore信号量

Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。
  使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

一个计数信号量。从概念上讲,信号量维护了一个许可集。
如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。拿到信号量的线程可以进入代码,否则就等待。

主要方法摘要:
   void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断
   void release():释放一个许可,将其返回给信号量
   int availablePermits():返回此信号量中当前可用的许可数
   boolean hasQueuedThreads():查询是否有线程正在等待获取
1.简单例子

 维护当前访问自身的线程个数    @Test
    public void semaphore1Test() throws InterruptedException {
        // 线程池
        ExecutorService exec = Executors.newCachedThreadPool();
        // 只能5个线程同时访问
        final Semaphore semp = new Semaphore(5);
        // 模拟20个客户端访问
        for (int index = 0; index < 20; index++) {
            final int NO = index;
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        // 请求获得许可,如果有可获得的许可则继续往下执行,许可数减1。否则进入阻塞状态
                        semp.acquire();
                        System.out.println("Accessing: " + NO);
                        Thread.sleep((long) (Math.random() * 10000));
                        // 访问完后,释放许可,许可数加1,如果屏蔽下面的语句,则在控制台只能打印5条记录,之后线程一直阻塞
                        semp.release();
                         System.out.println("线程" + Thread.currentThread().getName() +  "已离开,当前已有" + (3-semp.availablePermits()) + "个并发");
                    } catch (InterruptedException e) {
                    }
                }
            };
            exec.execute(run);
        }
        // 退出线程池
        exec.shutdown();
    }

2.当锁

当信号量的数量上限是1时,Semaphore可以被当做锁来使用。通过acquire和release方法来保护关键区域。

    @Test
    public void semaphore2Test() throws InterruptedException {
        final Business business = new Business();
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 3; i++) {
            executor.execute(new Runnable() {
                public void run() {
                    business.service();
                }
            }
            );
        }
        executor.shutdown();
    }
   class Business {
        private int count;
        Lock lock = new ReentrantLock();
        Semaphore sp = new Semaphore(1);
        public void service() {
            // lock.lock();
            try {
                sp.acquire(); // 当前线程使用count变量的时候将其锁住,不允许其他线程访问
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            try {
                count++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(count);
            } catch (RuntimeException e) {
                e.printStackTrace();
            } finally {
                // lock.unlock();
                sp.release(); // 释放锁
            }
        }
    }

3.生产者消费者模型,生成阻塞队列

a.消息通道

class SemaphoreQueue {
    private List<Integer> valueList;
    private Semaphore putActionNum;// 可以进行put操作的许可数量
    private Semaphore getActionNum;// 可以进行take操作的许可数量
    private Semaphore mutex;       //相当于锁 控制非线程安全的valueList的操作

    public SemaphoreQueue(int capacity) {
        putActionNum = new Semaphore(capacity);// 维护队列大小
        getActionNum = new Semaphore(0);       // 初始化时,队列为空,put操作许可数量为0
        mutex = new Semaphore(1);              // 用于保护非线程安全的valueList操作,用于并发生产时控制
        valueList = new ArrayList<Integer>(capacity);
    }

    public void put(Integer message) {
        try {
            putActionNum.acquire();// put操作许可减1
            mutex.acquire();
            valueList.add(message);
            mutex.release();
            getActionNum.release();// get操作许可加1
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public Integer take() {
        Integer message = null;
        try {
            getActionNum.acquire();// get操作许可减1
            mutex.acquire();
            if (valueList.size() > 0) {
                message = valueList.get(0);
                valueList.remove(0);
            } else {
                return null;
            }
            mutex.release();
            putActionNum.release();// put操作许可加1
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return message;
    }
}

b. 生产者和消费者

class Productor extends Thread {
    SemaphoreQueue queue;
    public Productor(SemaphoreQueue queue) {
        this.queue = queue;
    }

    public void run() {
        int i = 0;
        try {
            while (true) {
                i++;
                Integer message = new Integer(i);
                queue.put(message);
                if (i % 20 == 0) {
                    System.out.println("======== " + this.getName() + " 累计生产了 " + i + " 条消息  =======");
                    Thread.currentThread().sleep(1000);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Cousumertor extends Thread {
    SemaphoreQueue queue;
    public Cousumertor(SemaphoreQueue queue) {
        this.queue = queue;
    }
    public void run() {
        try {
            while (true) {
                Integer message = queue.take();
                if (message != null) {
                    System.out.println("======== " + this.getName() + " 消费消息:" + message + " =======");
                }
                Thread.currentThread().sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

c.测试

    public static void main(String[] args) {
        SemaphoreQueue queue = new SemaphoreQueue(20);
        // 开始生产
        Productor productor = new Productor(queue);
        productor.setName("生产者");
        productor.start();
        // 开始消费
        Cousumertor c1 = new Cousumertor(queue);
        c1.setName("消费者-c1");
        Cousumertor c2 = new Cousumertor(queue);
        c2.setName("消费者-c2");
        c1.start();
        c2.start();
    }

4. Semaphore vs. CountDownLatch
   相同点 :
      两者都是用于线程同步的工具类,都通过定义了一个继承AbstractQueuedSynchronizer的内部类Sync来实现具体的功能.
   不同点 :
  a. Semaphore提供了公平和非公平两种策略, 而CountDownLatch则不具备.
  b. CountDownLatch: 一个或者是一部分线程,等待另外一部线程都完成操作。
     Semaphorr: 维护一个许可集.通常用于限制可以访问某些资源(物理或逻辑的)的线程数目.

  c. CountDownLatch中计数是不能被重置的。CountDownLatch适用于一次同步。当使用CountDownLatch时,任何线程允许多次调用countDown(). 那些调用了await()方法的线程将被阻塞,直到那些没有被阻塞线程调用countDown()使计数到达0为止 。
  Semaphore允许线程获取许可, 未获得许可的线程需要等待.这样防止了在同一时间有太多的线程执行.Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到0。

  d. 使用CountDownLatch时,它关注的一个线程或者多个线程需要在其它在一组线程完成操作之后,在去做一些事情。比如:服务的启动等。使用Semaphore时,它关注的是某一个资源最多同时能被几个线程访问.

时间: 2024-10-26 21:01:59

thread_Semaphore信号量的相关文章

信号量与并发控制

代码1 - 未使用信号量控制并发: #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSUInt

Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例

概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3534050.html Semaphore简介 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可

同步原语之信号量

一.semaphore信号量分析 首先,说明信号量的作用,信号量的作用类似于自旋锁(其实,同步原语均有相似之处),相似之处在于,都有临界区,各进程需互斥访问,linux中提供了两种信号量的实现,有内核态的和用户态的,这里只介绍内核态的 相关数据结构 1 struct semaphore { 2 spinlock_t lock; 3 unsigned int count; 4 struct list_head wait_list; 5 }; 1 struct mutex { 2 /* 1: unl

线程同步(条件变量、信号量)以及死锁

死锁:指两个或两个以上进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待现象,若无外力作用,它们都将无法继续推进下去. 例:交叉死锁:线程1获得了锁1,线程2获得了锁2,此时线程1调用lock想获得锁2,需挂起等待线程2释放锁2,而线程2也想获得锁1,也需挂起等待线程1释放锁1,此时两个线程都挂起等待 产生死锁的四个必要条件: (1):互斥条件(一个资源每次只能被一个进程或线程使用) (2):请求与保持条件(一个进程或线程因请求资源而阻塞时,对已获得的资源不释放) (3):不剥夺条件(此

信号量 (线程互斥)

信号量:表示可用资源的数量,MUTEX是非0即为1的, 即,如果信号量描述的资源数目是1时,此时的信号量和互斥锁相同! 调用sem_wait()可以获得资源(P操作),使semaphore的值减1,如果调用sem_wait()时 semaphore的值已经是0,则挂起等待.如果不希望挂起等待,可以调用sem_trywait() .调用 sem_post() 可以释放资源(V操作),使semaphore 的值加1,同时唤醒挂起等待的线程. 1.生产者不会把消费者套个圈 2.消费者不会超过生产者  

秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题

版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题 在<秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量>中对经典多线程同步互斥问题进行了回顾和总结,这篇文章对Windows系统下常用的线程同步互斥机制——关键段.事件.互斥量.信号量进行了总结.有网友问到互斥量能处理“遗弃”问题,事件和信号量是否也能处理“遗弃”问题.因此本文将对事件和信号量作个试验,看看事件和信号量能否处理“遗弃”问题. 一.

semget 信号量创建

Linux进程间通信(六)---信号量通信之semget().semctl().semop()及其用法:http://www.educity.cn/linux/1241661.html 信号量 Linux函数 semget();semctl();semop();:http://blog.csdn.net/ta893115871/article/details/7505560 信号量函数 semget() semop() semctl():http://blog.chinaunix.net/uid

[笔记][Java7并发编程实战手册]3.2 资源的并发访问控制Semaphore信号量

[笔记][Java7并发编程实战手册]系列目录 简介 本文学习信号量Semaphore机制. Semaphore 本质是一个共享锁 内部维护一个可用的信号集,获取信号量之前需要先申请获取信号数量:用完之后,则需要释放信号量:如果不释放,那么其他等待线程则一直阻塞直到获取信号量或则被中断为止 本人的理解是:互斥锁是同一时间只能一个线程访问,而在这里,是同一时间允许获取到了信号量的线程并发访问,而没有获取到信号量的则必须等待信号量的释放: 将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,

20150218【改进信号量】IMX257实现GPIO-IRQ中断按键获取键值驱动程序

[改进信号量]IMX257实现GPIO-IRQ中断按键获取键值驱动程序 2015-02-18 李海沿 前面我们使用POLL查询方式来实现GPIO-IRQ按键中断程序 这里我们来使用信号量,让我们的驱动同时只能有一个应用程序打开. 一.首先在前面代码的基础上来一个简单的信号 1.定义一个全局的整形变量 2.在打开函数中,每次进入打开函数canopen都自减1, 3.当我们不使用时,在realease 中canopen自加1 4.这样就实现了一个简单的信号量,我们编译,测试 当我们使用两个应用程序来