JAVA线程同步 (三)信号量

一个信号量有且仅有3种操作,且它们全部是原子的:初始化、增加和减少 
增加可以为一个进程解除阻塞; 
减少可以让一个进程进入阻塞。

信号量维护一个许可集,若有必要,会在获得许可之前阻塞每一个线程: 
          //从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。 
          acquireUninterruptibly(int permits){} 
每一个release()添加一个许可,从而可能释放一个正在阻塞的获取者。 
Semaphore只对可用许可的号码进行计数,并采取相应的行动。 
  
如何获得Semaphore对象? 
    public Semaphore(int permits,boolean fair) 
    permits:初始化可用的许可数目。 
    fair: 若该信号量保证在征用时按FIFO的顺序授予许可,则为true,否则为false; 
    
如何从信号量获得许可? 
    public void acquire() throws InterruptedException

如何释放一个许可,并返回信号量? 
    public void release() 
    
代码实例: 
    20个人去银行存款,但是该银行只有两个办公柜台,有空位则上去存钱,没有空位则只能去排队等待

复制代码
package com.xhj.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 线程信号量Semaphore的运用
 * 
 * @author XIEHEJUN
 * 
 */
public class SemaphoreThread {
    private int a = 0;

    /**
     * 银行存钱类
     */
    class Bank {
        private int account = 100;

        public int getAccount() {
            return account;
        }

        public void save(int money) {
            account += money;
        }
    }

    /**
     * 线程执行类,每次存10块钱
     */
    class NewThread implements Runnable {
        private Bank bank;
        private Semaphore semaphore;

        public NewThread(Bank bank, Semaphore semaphore) {
            this.bank = bank;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            int b = a++;
            if (semaphore.availablePermits() > 0) {
                System.out.println("线程" + b + "启动,进入银行,有位置立即去存钱");
            } else {
                System.out.println("线程" + b + "启动,进入银行,无位置,去排队等待等待");
            }
            try {
                semaphore.acquire();
                bank.save(10);
                System.out.println(b + "账户余额为:" + bank.getAccount());
                Thread.sleep(1000);
                System.out.println("线程" + b + "存钱完毕,离开银行");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 建立线程,调用内部类,开始存钱
     */
    public void useThread() {
        Bank bank = new Bank();
        // 定义10个新号量
        Semaphore semaphore = new Semaphore(2);
        // 建立一个缓存线程池
        ExecutorService es = Executors.newCachedThreadPool();
        // 建立20个线程
        for (int i = 0; i < 10; i++) {
            // 执行一个线程
            es.submit(new Thread(new NewThread(bank, semaphore)));
        }
        // 关闭线程池
        es.shutdown();

        // 从信号量中获取两个许可,并且在获得许可之前,一直将线程阻塞
        semaphore.acquireUninterruptibly(2);
        System.out.println("到点了,工作人员要吃饭了");
        // 释放两个许可,并将其返回给信号量
        semaphore.release(2);
    }

    public static void main(String[] args) {
        SemaphoreThread test = new SemaphoreThread();
        test.useThread();
    }
}
复制代码

   

线程0启动,进入银行,有位置立即去存钱
线程4启动,进入银行,有位置立即去存钱
线程1启动,进入银行,有位置立即去存钱
线程2启动,进入银行,有位置立即去存钱
线程3启动,进入银行,有位置立即去存钱
线程6启动,进入银行,无位置,去排队等待等待
0账户余额为:120
4账户余额为:120
线程5启动,进入银行,有位置立即去存钱
线程7启动,进入银行,无位置,去排队等待等待
线程8启动,进入银行,无位置,去排队等待等待
线程9启动,进入银行,无位置,去排队等待等待
线程4存钱完毕,离开银行
线程0存钱完毕,离开银行
1账户余额为:130
2账户余额为:140
线程1存钱完毕,离开银行
线程2存钱完毕,离开银行
3账户余额为:150
6账户余额为:160
线程6存钱完毕,离开银行
线程3存钱完毕,离开银行
5账户余额为:170
7账户余额为:180
线程5存钱完毕,离开银行
线程7存钱完毕,离开银行
8账户余额为:190
9账户余额为:200
线程9存钱完毕,离开银行
线程8存钱完毕,离开银行
到点了,工作人员要吃饭了

思考: 
    在很多情况下,可能有多个线程需要访问数目很少的资源。假想在服务器上运行着若干个回答客户端请求的线程。这些线程需要连接到同一数据库,但任一时刻 
    只能获得一定数目的数据库连接。你要怎样才能够有效地将这些固定数目的数据库连接分配给大量的线程? 
    
答:1.给方法加同步锁,保证同一时刻只能有一个人去调用此方法,其他所有线程排队等待,但是此种情况下即使你的数据库链接有10个,也始终只有一个处于使

        用状态。这样将会大大的浪费系统资源,而且系统的运行效率非常的低下。


    2.另外一种方法当然是使用信号量,通过信号量许可与数据库可用连接数相同的数目,将大大的提高效率和性能。

原文地址:https://www.cnblogs.com/jiangzhaowei/p/7220715.html