Java中信号量Semaphore的使用

1. Semaphore的作用

在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一的线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的问题。在另外一种场景下,一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java提供了另外的并发访问控制–资源的多副本的并发访问控制,今天学习的信号量Semaphore即是其中的一种。

2. Semaphore实现原理初探

Semaphore是用来保护一个或者多个共享资源的访问,Semaphore内部维护了一个计数器,其值为可以访问的共享资源的个数。一个线程要访问共享资源,先获得信号量,如果信号量的计数器值大于1,意味着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。

如果计数器值为0,线程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量。

就好比一个厕所管理员,站在门口,只有厕所有空位,就开门允许与空侧数量等量的人进入厕所。多个人进入厕所后,相当于N个人来分配使用N个空位。为避免多个人来同时竞争同一个厕卫,在内部仍然使用锁来控制资源的同步访问。

Semaphore 有两个构造函数,参数为许可的个数 permits 和是否公平竞争 fair。通过 acquire 方法能够获得的许可个数为 permits,如果超过了这个个数,就需要等待。当一个线程 release 释放了一个许可后,fair 决定了正在等待的线程该由谁获取许可,如果是公平竞争则等待时间最长的线程获取,如果是非公平竞争则随机选择一个线程获取许可。不传 fair 的构造函数默认采用非公开竞争。

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

3. Semaphore的使用

Semaphore使用时需要先构建一个参数来指定共享资源的数量,Semaphore构造完成后即是获取Semaphore、共享资源使用完毕后释放Semaphore。

Semaphore semaphore = new Semaphore(10,true);
semaphore.acquire();
//do something here
semaphore.release();

示例:
下面的代码就是模拟控制商场厕所的并发使用。

public class ResourceManage {  
    private final Semaphore semaphore ;  
    private boolean resourceArray[];  
    private final ReentrantLock lock;  
    public ResourceManage() {  
        this.resourceArray = new boolean[10];//存放厕所状态  
        this.semaphore = new Semaphore(10,true);//控制10个共享资源的使用,使用先进先出的公平模式进行共享;公平模式的信号量,先来的先获得信号量  
        this.lock = new ReentrantLock(true);//公平模式的锁,先来的先选  
        for(int i=0 ;i<10; i++){  
            resourceArray[i] = true;//初始化为资源可用的情况  
        }  
    }  
    public void useResource(int userId){ 
		semaphore.acquire(); 
        try{  
            //semaphore.acquire();  
            int id = getResourceId();//占到一个坑  
            System.out.print("userId:"+userId+"正在使用资源,资源id:"+id+"\n");  
            Thread.sleep(100);//do something,相当于于使用资源  
            resourceArray[id] = true;//退出这个坑  
        }catch (InterruptedException e){  
            e.printStackTrace();  
        }finally {  
            semaphore.release();//释放信号量,计数器加1  
        }  
    }  
    private int getResourceId(){  
        int id = -1; 
		lock.lock();
        try {  
            //lock.lock();//虽然使用了锁控制同步,但由于只是简单的一个数组遍历,效率还是很高的,所以基本不影响性能。  
            for(int i=0; i<10; i++){  
                if(resourceArray[i]){  
                    resourceArray[i] = false;  
                    id = i;  
                    break;  
                }  
            }  
        }catch (Exception e){  
            e.printStackTrace();  
        }finally {  
            lock.unlock();  
        }  
        return id;  
    }  
}  
public class ResourceUser implements Runnable{  
    private ResourceManage resourceManage;  
    private int userId;  
    public ResourceUser(ResourceManage resourceManage, int userId) {  
        this.resourceManage = resourceManage;  
        this.userId = userId;  
    }  
    public void run(){  
        System.out.print("userId:"+userId+"准备使用资源...\n");  
        resourceManage.useResource(userId);  
        System.out.print("userId:"+userId+"使用资源完毕...\n");  
    }  
  
    public static void main(String[] args){  
        ResourceManage resourceManage = new ResourceManage();  
        Thread[] threads = new Thread[100];  
        for (int i = 0; i < 100; i++) {  
            Thread thread = new Thread(new ResourceUser(resourceManage,i));//创建多个资源使用者  
            threads[i] = thread;  
        }  
        for(int i = 0; i < 100; i++){  
            Thread thread = threads[i];  
            try {  
                thread.start();//启动线程  
            }catch (Exception e){  
                e.printStackTrace();  
            }  
        }  
    }  
}

最后,Semaphore除了控制资源的多个副本的并发访问控制,也可以使用二进制信号量来实现类似synchronized关键字和Lock锁的并发访问控制功能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值