Java中Semaphore(信号量)的使用

Semaphore的作用:

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

Semaphore实现原理初探:

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

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

Semaphore的使用:

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

[java] view plain copy

print?

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

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

import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.*;
import static net.mindview.util.Print.*;

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) throws InterruptedException {
        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");
        try {
            resourceManage.useResource(userId);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        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();
            }
        }
    }
userId:2准备使用资源...
userId:6准备使用资源...
userId:4准备使用资源...
userId:0准备使用资源...
userId:3准备使用资源...
userId:1准备使用资源...
userId:8准备使用资源...
userId:9准备使用资源...
userId:10准备使用资源...
userId:1正在使用资源,资源id:5
userId:3正在使用资源,资源id:4
userId:6正在使用资源,资源id:2
userId:0正在使用资源,资源id:3
userId:7准备使用资源...
userId:2正在使用资源,资源id:0
userId:4正在使用资源,资源id:1
userId:5准备使用资源...
userId:13准备使用资源...
userId:7正在使用资源,资源id:9
userId:12准备使用资源...
userId:11准备使用资源...
userId:10正在使用资源,资源id:8
userId:15准备使用资源...
userId:9正在使用资源,资源id:7
userId:8正在使用资源,资源id:6
userId:16准备使用资源...
userId:14准备使用资源...
userId:18准备使用资源...
userId:17准备使用资源...
userId:20准备使用资源...
userId:19准备使用资源...
userId:92准备使用资源...
userId:93准备使用资源...
userId:94准备使用资源...
userId:95准备使用资源...
userId:96准备使用资源...
userId:97准备使用资源...
userId:99准备使用资源...
userId:98准备使用资源...
userId:10使用资源完毕...
userId:5正在使用资源,资源id:0
userId:12正在使用资源,资源id:2
userId:16正在使用资源,资源id:5
userId:18正在使用资源,资源id:8
userId:17正在使用资源,资源id:9
userId:13正在使用资源,资源id:1
userId:3使用资源完毕...
userId:0使用资源完毕...
userId:1使用资源完毕...
userId:2使用资源完毕...
userId:4使用资源完毕...
userId:9使用资源完毕...
userId:7使用资源完毕...
userId:6使用资源完毕...

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

时间: 2024-10-13 04:27:40

Java中Semaphore(信号量)的使用的相关文章

Java中Semaphore(信号量) 数据库连接池

计数信号量用来控制同时访问某个特定资源的操作数或同时执行某个指定操作的数量 A counting semaphore.Conceptually, a semaphore maintains a set of permits. Each acquire blocks if necessary until a permit is available, and then takes it. Each release adds a permit, potentially releasing a bloc

java中的信号量Semaphore

       Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用.Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可.比如在Windows下可以设置共享文件的最大客户端访问个数. Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢

java中Semaphore

Semaphore是在当前在多线程环境下被扩放使用,单线程不可能使用,记住这是Semaphore使用的前提,必须是多线程环境下,Semaphore可以维护当前访问自身的线程个数,并提供了同步机制,使用Semaphore可以控制同时访问资源的线程个数,打个比方 厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了.另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺

java中的计数信号量(Counting Semaphore)

信号量(Semaphore)又称为信号量.旗语,它以一个整数变数,提供信号,以确保在并行计算环境中,不同进程在访问共享资源时,不会发生冲突.是一种不需要使用忙碌等待(busy waiting)的一种方法. 信号量的概念是由荷兰计算机科学家艾兹格·迪杰斯特拉(Edsger W. Dijkstra)发明的,广泛的应用于不同的操作系统中.在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来.如果信号量是一个任意的整数,通常被

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()来获取信号量的许可

java笔记--对信号量Semaphore的理解与运用

java Semaphore 信号量的使用: 在java中,提供了信号量Semaphore的支持. Semaphore类是一个计数信号量,必须由获取它的线程释放, 通常用于限制可以访问某些资源(物理或逻辑的)线程数目. 一个信号量有且仅有3种操作,且它们全部是原子的:初始化.增加和减少 增加可以为一个进程解除阻塞: 减少可以让一个进程进入阻塞. --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3910406.html "谢谢-- 信

Java多线程~~~使用信号量来控制资源获取

在多线程开发中,有一个很经典的名词,那就是信号量.信号量就是用来衡量一个资源的可利用数目的,根据信号 量的多少来控制在多线程中各个资源之间的冲突问题,在Java中也提供了对信号量的支持. 而且在创建信号量的时候,第二个参数用来指定采取何种分配策略,比如当有很多线程被阻塞,但有一个机会的时 候,信号量应该选择谁去运行呢,如果选择true,就采用公平模式,到时候看哪个线程等待的时间最久,就把机会给那 个等待最久的线程,这样的就是公平分配策略. 下面就用代码来说明一下问题 package com.bi

《java并发编程实战》读书笔记4--基础构建模块,java中的同步容器类&amp;并发容器类&amp;同步工具类,消费者模式

上一章说道委托是创建线程安全类的一个最有效策略,只需让现有的线程安全的类管理所有的状态即可.那么这章便说的是怎么利用java平台类库的并发基础构建模块呢? 5.1 同步容器类 包括Vector和Hashtable,此外还包括在JDK1.2中添加的一些功能相似的类,这些同步的封装器类由Collections.synchronizedXxx等工厂方法创建的.这些类实现线程安全的方式是:将他们的状态封装起来,并对每个共有方法都进行同步,使得每次只能有一个线程能访问容器的状态. 关于java中的Vect

Java 中的线程管理概念梳理

在Java中,"线程"指java.lang.Thread类的一个实例以及线程的执行,主要使用的线程池是ThreadPoolExecutor以及ScheduledThreadPoolExecutor,要使用固定线程上限的线程池. 用synchronized 修饰静态方法时,表示任何两个不同线程的调用互斥:修饰成员函数时,表示同一对象的多线程方法调用互斥:当然了,synchronized 后的参数可以是任意对象.Synchronized保证了synchronized块中变量的可见性,而vo