Java多线程之信号量(Semaphore)的使用

      

Semaphore是什么?

           Semaphore是Java提供的同步对象类,实现了经典的信号量,信号量是通过计数器控制对共享资源的访问。如果计数器大于0,允许访问资源,为0则拒绝访问。计数器计数允许共享资源的许可证,所以要访问资源线程必须保证获取信号量的许可证。通常,要使用信号量需要去访问共享资源的线程得尝试取得许可证,如果信号量的计算器大于0,就说明线程取得了许可证,这会导致信号量计数减小,否则,线程会被阻塞,直到能够获取许可证为止。 如果线程不需要访问共享资源时需要释放许可证。

           Semaphore类有下面两个构造方法:

                  Semaphore(int num)

                  Semaphore(int num,boolean how)

            其中,num指定了初始许可证的计数大小,比如num=1则表示任意时刻只有一个线程能够访问资源。默认情况下,等待线程以未定义的顺序获得许可证。通过将how设置为true,可以确定等待线程以它们要求的访问的顺序获得许可证。

           得到许可证可以通过调用acquire()方法,该方法有如下两张方式:

           void acquire()  throws InterrupetedException

           void acquire(int num)  throws InterrupetedException

           不传num表示获取一个许可证,传则表示获取num个许可证,

           释放许可证可以调用release()方法,如下:

           void release()

           void release(int num)

          

          线程在使用信号量的时候,必须先调用acquire()  ,当线程使用完资源后必须调用release(),具体看下面demo:

public class SemaphoreTest {

    //共享资源类
    static class Shared{

        static int count = 0;

    }

    static class ThreadOne implements Runnable{

        String name;
        Semaphore semaphore;

        public ThreadOne(String name,Semaphore sem) {
            this.name = name;
            this.semaphore = sem;
            new Thread(this).start();
        }

        @Override
        public void run() {

            System.out.println("开始:"+name);
            try {
                System.out.println(name + "等待获取一个许可证");
                semaphore.acquire();
                System.out.println(name + "获取了一个许可证");

                for (int i = 0; i <5 ; i++) {
                    Shared.count++;
                    System.out.println(name+ ":"+Shared.count);
                    Thread.sleep(10);
                }
            }catch (Exception e){

            }
            System.out.println(name + "释放许可证");
            semaphore.release();

        }
    }


    static class ThreadTwo implements Runnable{

        String name;
        Semaphore semaphore;

        public ThreadTwo(String name,Semaphore sem) {
            this.name = name;
            this.semaphore = sem;
            new Thread(this).start();
        }

        @Override
        public void run() {

            System.out.println("开始:"+name);
            try {
                System.out.println(name + "等待获取一个许可证");
                semaphore.acquire();
                System.out.println(name + "获取了一个许可证");

                for (int i = 0; i <5 ; i++) {
                    Shared.count--;
                    System.out.println(name+ ":"+Shared.count);
                    Thread.sleep(10);
                }
            }catch (Exception e){

            }
            System.out.println(name + "释放许可证");
            semaphore.release();

        }
    }
}

 打印结果:

开始:线程One
线程One等待获取一个许可证
线程One获取了一个许可证
开始:线程Two
线程Two等待获取一个许可证
线程One:1
线程One:2
线程One:3
线程One:4
线程One:5
线程One释放许可证
线程Two获取了一个许可证
线程Two:4
线程Two:3
线程Two:2
线程Two:1
线程Two:0
线程Two释放许可证


使用信号量实现生产者与消费者


          一般情况下多线程间的同步协作会使用synchronized关键字,并且结合wait()与notify()来实现同步,如下代码实现:

public class ThreadTest {


    static class Queue{

        int count;
        boolean valueSet ;

        synchronized int get(){
            while (!valueSet){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("消费:"+ count);
            valueSet = false;
            notify();
            return  count;
        }

        synchronized void put(int count){
            while (valueSet){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.count = count;
            System.out.println("生产:"+ count);
            valueSet = true;
            notify();
        }

    }


    static class Consumer implements Runnable{

        Queue q;

        public Consumer(Queue q) {
            this.q = q;
            new Thread(this,"消费者").start();
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                q.get();
            }
        }
    }

    static class Product implements Runnable{

        Queue q;
        public Product(Queue q) {
            this.q = q;
            new Thread(this,"生产者:").start();
        }

        @Override
        public void run() {

            for (int i = 0; i < 10; i++) {
                q.put(i);
            }
        }
    }

    public static void main(String[] args) {
        Queue q = new Queue();

        new Product(q);
        new Consumer(q);
    }
}

输出结果:

生产:0
消费:0
生产:1
消费:1
生产:2
消费:2
生产:3
消费:3
生产:4
消费:4

....


使用信号量实现代码如下:

public class SemaphoreTest {


    static class Queue{

        int count;

        Semaphore conSem = new Semaphore(0);
        Semaphore proSem = new Semaphore(1);

        void get(){
            try {
                conSem.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("消费:"+ count);
            proSem.release();
        }

        void put(int count){
            try {
                proSem.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            this.count = count;
            System.out.println("生产:"+ count);
            conSem.release();
        }

    }


    static class Consumer implements Runnable{

        Queue q;

        public Consumer(Queue q) {
            this.q = q;
            new Thread(this,"消费者").start();
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                q.get();
            }
        }
    }

    static class Product implements Runnable{

        Queue q;
        public Product(Queue q) {
            this.q = q;
            new Thread(this,"生产者:").start();
        }

        @Override
        public void run() {

            for (int i = 0; i < 10; i++) {
                q.put(i);
            }
        }
    }

    public static void main(String[] args) {
        Queue q = new Queue();

        new Product(q);
        new Consumer(q);
    }
}

打印的结果和上面一样。


注意:conSem初始化时没有设置许可证,这样可以保证首页执行put()。

猜你喜欢

转载自blog.csdn.net/aidesudi/article/details/6798930