Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 280|回复: 0

[Java线程学习]并发编程:信号量入门

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-11-5 00:00:03 | 显示全部楼层 |阅读模式
    一、信号量       信号量通过一个计数器控制对共享资源的访问。如果计数器大于0,则访问被允许,如果为0,则访问被禁止。 计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果
    另一个线程等待通行证,则那个线程将在那时获得通行证。java的Semaphore类实现了这种机制。  
    Semaphore的两个构造函数
    Semaphore(int num)
    Semaphore(int num,boolean how)
      
       
       
         
       

         
       
      
         num指定初始通行证计数。因此,num指定了每次能够访问共享资源的线程数,如果num是1,则只有一个线程每次都能够访问共享资源。默认情况下,以未定义次序授予等待线程的通行证,如果将how设为true, 可确保以请求访问的次序授予等待线程的通行证。  要获得通行证,调用acquire()方法 void acquire() throws InterruptedException
    void acquire(int num) throws InterruptedException 第一种形式获得一个通行证,第二种形式获得num个通行证。如果该通行证无法在调用时授予,则调用线程暂停,直到获得通行证。  要释放通行证,调用repease方法,
    void release()
    void release(int num) 分别释放一个或num个通行证。 为了用一个信号量控制对资源的访问,使用该资源的每个线程在访问该资源之前,首先要调用acquire(), 当一个线程结束对资源的使用后,它必须调用release()。
      二、下面的例子示范如何使用信号量:
    1. import java.util.concurrent.*;

    2. class SemDemo {

    3.   public static void main(String args[]) {
    4.     Semaphore sem = new Semaphore(1);

    5.     new IncThread(sem, "A");
    6.     new DecThread(sem, "B");

    7.   }
    8. }

    9. // A shared resource.
    10. class Shared {
    11.   static int count = 0;
    12. }

    13. // A thread of execution that increments count.
    14. class IncThread implements Runnable {
    15.   String name;
    16.   Semaphore sem;

    17.   IncThread(Semaphore s, String n) {
    18.     sem = s;
    19.     name = n;
    20.     new Thread(this).start();
    21.   }

    22.   public void run() {
    23.      
    24.     System.out.println("Starting " + name);

    25.     try {
    26.       // First, get a permit.
    27.       System.out.println(name + " is waiting for a permit.");
    28.       sem.acquire();
    29.       System.out.println(name + " gets a permit.");

    30.       // Now, access shared resource.
    31.       for(int i=0; i < 5; i++) {
    32.         Shared.count++;
    33.         System.out.println(name + ": " + Shared.count);

    34.         // Now, allow a context switch -- if possible.
    35.         Thread.sleep(10);
    36.       }
    37.     } catch (InterruptedException exc) {
    38.       System.out.println(exc);
    39.     }

    40.     // Release the permit.
    41.     System.out.println(name + " releases the permit.");
    42.     sem.release();
    43.   }
    44. }

    45. // A thread of execution that deccrements count.
    46. class DecThread implements Runnable {
    47.   String name;
    48.   Semaphore sem;

    49.   DecThread(Semaphore s, String n) {
    50.     sem = s;
    51.     name = n;
    52.     new Thread(this).start();
    53.   }

    54.   public void run() {
    55.      
    56.     System.out.println("Starting " + name);

    57.     try {
    58.       // First, get a permit.
    59.       System.out.println(name + " is waiting for a permit.");
    60.       sem.acquire();
    61.       System.out.println(name + " gets a permit.");

    62.       // Now, access shared resource.
    63.       for(int i=0; i < 5; i++) {
    64.         Shared.count--;
    65.         System.out.println(name + ": " + Shared.count);

    66.         // Now, allow a context switch -- if possible.
    67.         Thread.sleep(10);
    68.       }
    69.     } catch (InterruptedException exc) {
    70.       System.out.println(exc);
    71.     }

    72.     // Release the permit.
    73.     System.out.println(name + " releases the permit.");
    74.     sem.release();
    75.   }
    76. }
    77. 程序的输出:
    78. C:java>java   SemDemo
    79. Starting A
    80. A is waiting for a permit.
    81. A gets a permit.
    82. A: 1
    83. Starting B
    84. B is waiting for a permit.
    85. A: 2
    86. A: 3
    87. A: 4
    88. A: 5
    89. A releases the permit.
    90. B gets a permit.
    91. B: 4
    92. B: 3
    93. B: 2
    94. B: 1
    95. B: 0
    96. B releases the permit.
    复制代码
       这个程序使用信号量控制对Shared类中静态变量count的访问,为了防止这两个线程同时访问Shared.count,仅当线程从控制信号量获得通行证之后才允许访问,访问结束后,该通行证被释放。通过这种方式,保证一次只有一个线程访问Shared.count.        在run()中,sleep()的调用导致调用线程每次访问Shared.count之后都暂停,这通常会使第二个线程运行, 但在这里,由于信号量的原因,第二个线程必须等待第一个线程释放通行证,而这要等到第一个线程对共享资源的所有访问都结束之后才能发生。
      三、使用两个信号量同步生产者和消费者线程。
    1. import java.util.concurrent.Semaphore;

    2. class Q { //仓库
    3.   int n; //仓库中的产品

    4.   // Start with consumer semaphore unavailable.
    5.   static Semaphore semCon = new Semaphore(0);
    6.   static Semaphore semProd = new Semaphore(1);

    7.   void get() {
    8.     try {
    9.       semCon.acquire();
    10.     } catch(InterruptedException e) {
    11.       System.out.println("InterruptedException caught");
    12.     }

    13.      System.out.println("Got: " + n);
    14.      semProd.release();
    15.   }

    16.   void put(int n) {
    17.     try {
    18.       semProd.acquire();
    19.     } catch(InterruptedException e) {
    20.       System.out.println("InterruptedException caught");
    21.     }

    22.     this.n = n;
    23.     System.out.println("Put: " + n);
    24.     semCon.release();
    25.   }
    26. }

    27. class Producer implements Runnable { //生产者
    28.   Q q;

    29.   Producer(Q q) {
    30.     this.q = q;
    31.     new Thread(this, "Producer").start();
    32.   }

    33.   public void run() {
    34.     for(int i=0; i < 20; i++) q.put(i);
    35.   }
    36. }

    37. class Consumer implements Runnable { //消费者
    38.   Q q;

    39.   Consumer(Q q) {
    40.     this.q = q;
    41.     new Thread(this, "Consumer").start();
    42.   }

    43.   public void run() {
    44.     for(int i=0; i < 20; i++)  q.get();
    45.   }
    46. }

    47. class ProdCon { //测试类
    48.   public static void main(String args[]) {
    49.     Q q = new Q();
    50.     new Consumer(q);
    51.     new Producer(q);
    52.   }
    53. }
    54. 程序的部分输出:

    55. C:java>java  ProdCon
    56. Put: 0
    57. Got: 0
    58. Put: 1
    59. Got: 1
    60. Put: 2
    61. Got: 2
    62. Put: 3
    63. Got: 3
    64. Put: 4
    65. Got: 4
    66. Put: 5
    67. Got: 5
    68. Put: 6
    69. Got: 6
    70. Put: 7
    71. Got: 7
    72. Put: 8
    73. Got: 8
    74. Put: 9
    75. Got: 9
    76. Put: 10
    77. Got: 10
    复制代码
        可以看到,put()调用和get()调用是同步的。每次调用一个put()都会随后调用一个get(),不会有失配的值。如果没有信号量,put()的多次调用可能会没有与其匹配的get()调用。从而导致出现失配的值,可删除信号量代码验证。     put()和get()调用通过两个信号量来处理:semProd和semCon。在put()生成一个值之前,它必须从semProd获得一个通行证。它设置该值后释放semCon。在get()使用一个值之前,它必须从semCon获得一个通过证。它使用该值后,必须释放semPord。这种“给与受”的机制确保put()的每次调用必须跟随一个get()调用。     semCon在初始化的时候无法获得通行证,从而保证了put()首先运行。设置初始同步状态的能力是信号量的强大表现之一。

      
      
       
       

         
       

         
       
      
    复制代码

    源码下载:http://file.javaxxz.com/2014/11/5/000002515.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-25 13:19 , Processed in 0.305211 second(s), 36 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表