美文网首页
Java 并发专题:Semaphore 实现 互斥 与 连接池

Java 并发专题:Semaphore 实现 互斥 与 连接池

作者: 暖熊熊 | 来源:发表于2017-09-30 09:06 被阅读0次

    Semaphore,位于java.util.concurrent包下面

    • Semaphore中管理着一组虚拟的许可,许可的初始数量可通过构造函数来指定new Semaphore(1);,执行操作时可以首先获得许可semaphore.acquire();,并在使用后释放许可semaphore.release();。如果没有许可,那么acquire方法将会一直阻塞直到有许可(或者直到被终端或者操作超时)。
    • Semaphore是一种在多线程环境下使用的设施,该设施负责协调各个线程,以保证它们能够正确、合理的使用公共资源的设施,也是操作系统中用于控制进程同步互斥的量。
    • Semaphore分为单值和多值两种,前者只能被一个线程获得,后者可以被若干个线程获得。
      作用:可以用来控制同时访问某个特定资源的操作数量,或者某个操作的数量。

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

    1. 互斥
      大家都学过操作系统,都知道互斥的概念,比较简单的互斥实现,比如PV操作,判断资源,然后忙等实现互斥;上一篇也说过,忙等对CPU的消耗巨大,下面我们通过Semaphore来实现一个比较好的互斥操作:
      假设我们公司只有一台打印机,我们需要对这台打印机的打印操作进行互斥控制:
      首先是没有使用Semaphore来实现互斥操作
    public class Test {
        public void print(String str) throws InterruptedException {
            System.out.println(Thread.currentThread().getName()+"准备完成,开始打印...");
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
            System.out.println(Thread.currentThread().getName()+"打印完成,退出程序 ...");
        }
    
        public static void main(String[] args) {
            Test t = new Test();
            
            for(int i = 0; i < 10 ; i++) {
                new Thread() {
                    public void run() {
                        try {
                            t.print("hello");
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        }
    }
    

    运行结果:

    没有Semaphore

    从结果可以看出,一个Thread还没有打印完成,退出程序,别的Thread就进入开始打印了,这样必然没有按照正确的流程来打印。

    使用Semaphore来控制互斥访问数

    import java.util.concurrent.Semaphore;
    /**
     * 使用Semphore实现互斥访问打印机
     * @author ghw
     *
     */
    public class Test {
        //定义初始值为1的信号量 
        private Semaphore semaphore = new Semaphore(1);
        //模拟打印机打印操作
        public void print(String str) throws InterruptedException {
            //请求许可
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName()+"准备完成,开始打印...");
            //模拟打印时间
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
            System.out.println(Thread.currentThread().getName()+"打印完成,退出程序 ...");
            //释放许可
            semaphore.release();
        }
    
        public static void main(String[] args) {
            Test t = new Test();
            //开启10个线程执行打印任务
            for(int i = 0; i < 10 ; i++) {
                new Thread() {
                    public void run() {
                        try {
                            t.print("hello");
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        }
    }
    

    运行结果:

    使用Semphore

    从结果可以看出,多个线程虽然是无序执行的,但每个线程执行完成后退出程序才开始执行下一个线程,这样就不会乱。

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.Semaphore;
    
    public class ConnectPool {
        
        private final List<Conn> pool = new ArrayList<Conn>(3);  
        private Semaphore semaphore = new Semaphore(3);
    
        
        public ConnectPool() {
            pool.add(new Conn());
            pool.add(new Conn());
            pool.add(new Conn());
        }
        
        public Conn getConn() throws InterruptedException {
            semaphore.acquire();  
            Conn c = null  ;  
            synchronized (pool)  
            {  
                c = pool.remove(0);  
            }  
            System.out.println(Thread.currentThread().getName()+" 获得一个连接 " + c);  
            return c ;  
        }
        
        public void release(Conn c) {
            pool.add(c);  
            System.out.println(Thread.currentThread().getName()+" 释放了一个连接 " + c);  
            semaphore.release();  
        }
        
        public static void main(String[] args) {
            ConnectPool pool = new ConnectPool();
            
            new Thread() {
                public void run() {
                    try {
                        Conn c = pool.getConn();
                        Thread.sleep(3000);
                        pool.release(c);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                };
            }.start();
            
            for(int i=0;i<5;i++) {
                 new Thread() {
                    public void run() {
                        try {
                            Conn c = pool.getConn();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    };
                    
                 }.start();
            }
        }
    }
    
    

    运行结果:

    运行结果3

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

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

    相关文章

      网友评论

          本文标题:Java 并发专题:Semaphore 实现 互斥 与 连接池

          本文链接:https://www.haomeiwen.com/subject/bihgextx.html