Semaphore 的理解

转载博客:http://blog.csdn.net/lmj623565791/article/details/26810813

今天介绍Semaphore,同样在Java.util.concurrent包下。本来准备通过例子,从自己实现到最后使用并发工具实现,但是貌似效果并不是很好,有点太啰嗦的感觉,所有准备直入主题。

介绍:Semaphore中管理着一组虚拟的许可,许可的初始数量可通过构造函数来指定【new Semaphore(1);】,执行操作时可以首先获得许可【semaphore.acquire();】,并在使用后释放许可【semaphore.release();】。如果没有许可,那么acquire方法将会一直阻塞直到有许可(或者直到被终端或者操作超时)。

作用:可以用来控制同时访问某个特定资源的操作数量,或者某个操作的数量。

下面使用Semaphore实现两个例子:


1、互斥

大家都学过操作系统,都知道互斥的概念,比较简单的互斥实现,比如PV操作,判断资源,然后忙等实现互斥;上一篇博客也说过,忙等对CPU的消耗巨大,下面我们通过Semaphore来实现一个比较好的互斥操作:

假设我们公司只有一台打印机,我们需要对这台打印机的打印操作进行互斥控制:

  package com.zhy.concurrency.semaphore;  
      
    import java.util.concurrent.Semaphore;  
      
    /** 
     * 使用信号量机制,实现互斥访问打印机 
     *  
     * @author zhy 
     *  
     */  
    public class MutexPrint  
    {  
      
        /** 
         * 定义初始值为1的信号量 
         */  
        private final Semaphore semaphore = new Semaphore(1);  
      
        /** 
         * 模拟打印操作 
         * @param str 
         * @throws InterruptedException 
         */  
        public void print(String str) throws InterruptedException  
        {  
            //请求许可  
            semaphore.acquire();  
              
            System.out.println(Thread.currentThread().getName()+" enter ...");  
            Thread.sleep(1000);  
            System.out.println(Thread.currentThread().getName() + "正在打印 ..." + str);  
            System.out.println(Thread.currentThread().getName()+" out ...");  
            //释放许可  
            semaphore.release();  
        }  
      
        public static void main(String[] args)  
        {  
            final MutexPrint print = new MutexPrint();  
      
            /** 
             * 开启10个线程,抢占打印机 
             */  
            for (int i = 0; i < 10; i++)  
            {  
                new Thread()  
                {  
                    public void run()  
                    {  
                        try  
                        {  
                            print.print("helloworld");  
                        } catch (InterruptedException e)  
                        {  
                            e.printStackTrace();  
                        }  
                    };  
                }.start();  
            }  
      
        }  
      
    }  

输出结果:

    Thread-1 enter ...  
    Thread-1正在打印 ...helloworld  
    Thread-1 out ...  
    Thread-2 enter ...  
    Thread-2正在打印 ...helloworld  
    Thread-2 out ...  
    Thread-0 enter ...  
    Thread-0正在打印 ...helloworld  
    Thread-0 out ...  
    Thread-3 enter ...  
    Thread-3正在打印 ...helloworld  
    Thread-3 out ...  

通过初始值为1的Semaphore,很好的实现了资源的互斥访问。


2、连接池的模拟实现

在项目中处理高并发时,一般数据库都会使用数据库连接池,假设现在数据库连接池最大连接数为10,当10个连接都分配出去以后,现在有用户继续请求连接,可能的处理:

a、手动抛出异常,用户界面显示,服务器忙,稍后再试

b、阻塞,等待其他连接的释放

从用户体验上来说,更好的选择当然是阻塞,等待其他连接的释放,用户只会觉得稍微慢了一点,并不影响他的操作。下面使用Semaphore模拟实现一个数据库连接池:

    package com.zhy.concurrency.semaphore;  
      
    import java.util.ArrayList;  
    import java.util.List;  
    import java.util.concurrent.Semaphore;  
    /** 
     * 使用Semaphore模拟数据库链接池的使用 
     * @author zhy 
     * 
     */  
    public class ConnectPool  
    {  
        private final List<Conn> pool = new ArrayList<Conn>(3);  
        private final Semaphore semaphore = new Semaphore(3);  
      
        /** 
         * 初始化分配3个连接 
         */  
        public ConnectPool()  
        {  
            pool.add(new Conn());  
            pool.add(new Conn());  
            pool.add(new Conn());  
        }  
      
        /** 
         * 请求分配连接 
         * @return 
         * @throws InterruptedException 
         */  
        public Conn getConn() throws InterruptedException  
        {  
            semaphore.acquire();  
            Conn c = null  ;  
            synchronized (pool)  
            {  
                c = pool.remove(0);  
            }  
            System.out.println(Thread.currentThread().getName()+" get a conn " + c);  
            return c ;  
        }  
          
        /** 
         * 释放连接 
         * @param c 
         */  
        public void release(Conn c)  
        {  
            pool.add(c);  
            System.out.println(Thread.currentThread().getName()+" release a conn " + c);  
            semaphore.release();  
        }  
      
        public static void main(String[] args)  
        {  
      
            final ConnectPool pool = new ConnectPool();  
              
            /** 
             * 第一个线程占用1个连接3秒 
             */  
            new Thread()  
            {  
                public void run()  
                {  
                    try  
                    {  
                        Conn c = pool.getConn();  
                        Thread.sleep(3000);  
                        pool.release(c);  
                    } catch (InterruptedException e)  
                    {  
                        e.printStackTrace();  
                    }  
                };  
            }.start();  
            /** 
             * 开启3个线程请求分配连接 
             */  
            for (int i = 0; i < 3; i++)  
            {  
                new Thread()  
                {  
                    public void run()  
                    {  
                        try  
                        {  
                            Conn c = pool.getConn();  
                        } catch (InterruptedException e)  
                        {  
                            e.printStackTrace();  
                        }  
                    };  
                }.start();  
            }  
      
        }  
      
        private class Conn  
        {  
        }  
      
    }  

输出结果

Thread-0 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651  
Thread-2 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@e53108  
Thread-1 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@1888759  
Thread-0 release a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651  
Thread-3 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651 

我们测试时,让Thread-0持有一个连接3秒,然后瞬间让3个线程再去请求分配连接,造成Thread-3一直等到Thread-0对连接的释放,然后获得连接。


通过两个例子,基本已经了解了Semaphore的用法,这里的线程池例子只是为了说明Semaphore的用法,真实的实现代码比这复杂的多,而且可能也不会直接用Semaphore。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值