博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java Concurrent包学习笔记(五):Semaphore
阅读量:6140 次
发布时间:2019-06-21

本文共 7315 字,大约阅读时间需要 24 分钟。

一、Semaphore 是什么 

信号量Semaphore是一个并发工具类,用来控制可同时并发的线程数,其内部维护了一组虚拟许可,构造函数初始化的时候可以指定许可的总数量

每次线程执行操作时先通过acquire方法获得许可,线程获得许可后Semaphore 的许可数量会减1,执行完毕再通过release方法释放许可,emaphore 的许可数量会加1。如果无可用许可,那么acquire方法将一直阻塞,直到其它线程释放许可。

主要方法:

  • Semaphore(int permits): 构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。
  • Semaphore(int permits,boolean fair): 构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。
  • void acquire(): 从此信号量获取一个许可,如果没有获取到许可线程将一直阻塞。
  • void acquire(int n): 从此信号量获取给定数目许可,在提供这些许可前一直将线程阻塞。
  • void release(): 释放一个许可,将其返回给信号量。就如同车开走返回一个车位。
  • void release(int n): 释放n个许可。
  • int availablePermits():当前可用的许可数

二、Semaphore 和线程池的区别

  • 线程池:用来控制实际工作的线程数量,通过线程复用的方式来减小内存开销。线程池可同时工作的线程数量是一定的,超过该数量的线程需进入线程队列等待,直到有可用的工作线程来执行任务。
  • Semaphore: 你创建了多少线程,实际就会有多少线程进行执行,只是可同时执行的线程数量会受到限制。但使用线程池,不管你创建多少线程,实际可执行的线程数是一定的。

示例程序:

package semaphore.demo;import java.util.UUID;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;/** * @author boshen * @date 2018/12/20 */public class SemaphoreTest1 {    /**     * 用线程池来控制工作线程     */    private static void testThreadPoll(){        ExecutorService executorService = Executors.newFixedThreadPool(2);        for(int i = 0;i< 5;i++)            executorService.submit(new Runnable() {                public void run() {                    try {                        String name = UUID.randomUUID().toString().substring(4,10);                        System.out.println("学生:"+name +" 开始工作,线程号为:"+Thread.currentThread().getName());                        Thread.sleep(2000);                        System.out.println("学生:"+name +" 完成工作,线程号为:"+Thread.currentThread().getName());                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });        executorService.shutdown();    }    /**     * 用线程池来控制工作线程     */    private static void testSemaphore(){        final Semaphore semaphore = new Semaphore(2);        for(int i=0;i<5;i++){            Thread thread = new Thread(new Runnable() {                public void run() {                    try {                        semaphore.acquire();                        String name = UUID.randomUUID().toString().substring(4,10);                        System.out.println("学生:"+name +" 开始工作,线程号为:"+Thread.currentThread().getName());                        Thread.sleep(2000);                        System.out.println("学生:"+name +" 完成工作,线程号为:"+Thread.currentThread().getName());                        semaphore.release();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });            thread.start();        }    }    public static void main(String[] args){        System.out.println("以下是testThreadPoll输出的结果============================================");        testThreadPoll();        try {            Thread.sleep(20000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("以下是testSemaphore输出的结果============================================");        testSemaphore();    }}
以下是testThreadPoll输出的结果============================================学生:14fd-7 开始工作,线程号为:pool-1-thread-1学生:6bc6-6 开始工作,线程号为:pool-1-thread-2学生:6bc6-6 完成工作,线程号为:pool-1-thread-2学生:6a3b-6 开始工作,线程号为:pool-1-thread-2学生:14fd-7 完成工作,线程号为:pool-1-thread-1学生:9e7b-9 开始工作,线程号为:pool-1-thread-1学生:6a3b-6 完成工作,线程号为:pool-1-thread-2学生:2ed3-4 开始工作,线程号为:pool-1-thread-2学生:9e7b-9 完成工作,线程号为:pool-1-thread-1学生:2ed3-4 完成工作,线程号为:pool-1-thread-2以下是testSemaphore输出的结果============================================学生:8121-6 开始工作,线程号为:Thread-1学生:99e9-7 开始工作,线程号为:Thread-0学生:99e9-7 完成工作,线程号为:Thread-0学生:8121-6 完成工作,线程号为:Thread-1学生:b9a5-7 开始工作,线程号为:Thread-3学生:058d-3 开始工作,线程号为:Thread-2学生:b9a5-7 完成工作,线程号为:Thread-3学生:7f6a-c 开始工作,线程号为:Thread-4学生:058d-3 完成工作,线程号为:Thread-2学生:7f6a-c 完成工作,线程号为:Thread-4

由以上结果可知当使用前线池的时候,同时只有2个线程执行工作,线程号只会有1和2,线程会进行复用;Semaphore虽然也只有2个线程能同时工作,但是线程号是0、1、2、3、4,也就是5个线程都执行了,只不过同一时刻有三个线程阻塞了而已。

 

三、Semaphore 公平信号量和非公平信号量

示例:

package semaphore.demo;import java.util.UUID;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;/** * @author boshen * @date 2018/12/20 */public class SemaphoreTest2 {    /**     * 公平信号量     */    private static void testFairSemaphore(){        final Semaphore semaphore1 = new Semaphore(2,true);        for(int i=0;i<5;i++){            Thread thread = new Thread(new Runnable() {                public void run() {                    try {                        String name = UUID.randomUUID().toString().substring(4,10);                        System.out.println("学生:"+name +" 开始工作,线程号为:"+Thread.currentThread().getName());                        semaphore1.acquire();                        Thread.sleep(1000);                        System.out.println("学生:"+name +" 完成工作,线程号为:"+Thread.currentThread().getName());                        semaphore1.release();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });            thread.start();        }    }    /**     * 非公平信号量     */    private static void testUnFairSemaphore(){        final Semaphore semaphore2 = new Semaphore(2);        for(int i=0;i<5;i++){            final Thread thread = new Thread(new Runnable() {                public void run() {                    try {                        String name = UUID.randomUUID().toString().substring(4,10);                        System.out.println("学生:"+name +" 开始工作,线程号为:"+Thread.currentThread().getName());                        semaphore2.acquire();                        Thread.sleep(1000);                        System.out.println("学生:"+name +" 完成工作,线程号为:"+Thread.currentThread().getName());                        semaphore2.release();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });            thread.start();        }    }    public static void main(String[] args){        System.out.println("以下是testFairSemaphore输出的结果============================================");        testFairSemaphore();        try {            Thread.sleep(10000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("以下是testUnFairSemaphore输出的结果============================================");        testUnFairSemaphore();    }}
以下是testFairSemaphore输出的结果============================================学生:81f2-0 开始工作,线程号为:Thread-0学生:6981-d 开始工作,线程号为:Thread-1学生:e212-a 开始工作,线程号为:Thread-4学生:f059-d 开始工作,线程号为:Thread-2学生:9ece-5 开始工作,线程号为:Thread-3学生:81f2-0 完成工作,线程号为:Thread-0学生:e212-a 完成工作,线程号为:Thread-4学生:6981-d 完成工作,线程号为:Thread-1学生:f059-d 完成工作,线程号为:Thread-2学生:9ece-5 完成工作,线程号为:Thread-3以下是testUnFairSemaphore输出的结果============================================学生:9552-1 开始工作,线程号为:Thread-5学生:4908-0 开始工作,线程号为:Thread-7学生:e920-5 开始工作,线程号为:Thread-6学生:be7a-f 开始工作,线程号为:Thread-9学生:e4fc-b 开始工作,线程号为:Thread-8学生:4908-0 完成工作,线程号为:Thread-7学生:e920-5 完成工作,线程号为:Thread-6学生:9552-1 完成工作,线程号为:Thread-5学生:e4fc-b 完成工作,线程号为:Thread-8学生:be7a-f 完成工作,线程号为:Thread-9

公平信号量的效果:哪信线程首先调用Semaphore.acquire(),哪个线程优先获得许可,首先启动的线程优先获取许可(不是100%,只是增加了概率)

非公平信号量的效果:是与线程的启动顺序与调用Semaphore.acquire()顺序无关,也就是说先启动的线程并不一定先获得许可。

 

转载地址:http://srkya.baihongyu.com/

你可能感兴趣的文章
添加用户名到sudoers中
查看>>
HttpClient 4 教程 第1章 基础
查看>>
金日开博
查看>>
Zoj2100--Seeding(Dfs)
查看>>
cf-Area of a Star
查看>>
字典树Trie的使用
查看>>
WPF 异步显示
查看>>
hihocoder 1465 循环串匹配问题(后缀自动机)
查看>>
使用XStream对Java对象进行序列化和反序列化
查看>>
注解(Annotation)自定义注解入门
查看>>
强制链接静态库所有符号(包括未被使用的)
查看>>
Asp.net获取系统信息
查看>>
【mysql】利用全文索引实现中文的快速查找
查看>>
Defining as a "long" or "int" type throws an error on startup
查看>>
在多台服务器上简单实现Redis的数据主从复制
查看>>
Html5 冒泡排序演示
查看>>
js 学习路线
查看>>
前端编辑工具有感
查看>>
CentOS6启动流程
查看>>
SpringBoot项目修改html后不即时编译
查看>>