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入门到精通教程
查看: 258|回复: 0

[Java线程学习]jdk5中的多线程

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

    [LV.1]初来乍到

    发表于 2014-11-4 23:59:57 | 显示全部楼层 |阅读模式
    JDK6 已经发布了,而对于JDK5新特性还来不及使用,虽然在项目中还没有使用,但可以写一些Demo体验一下Tiger的魅力,现在的时代就是体验的时代,事事都要亲历亲为才能有发言权,怎么有点毛主席“实事求是”的感觉。 JDK5中的一个亮点就是将Doug Lea并发库引入到java标准库中。Doug Lea确实是一个牛人,能教书,能出书,能编码,不过这在国外还是比较普遍的,而国内的教授们就相差太远了。 一般的服务器都需要线程池,比如Web、FTP等服务器,不过它们一般都自己实现了线程池,比如以前介绍过的Tomcat、Resin和Jetty等,现在有了JDK5,我们就没有必要重复造车轮了,直接使用就可以,何况使用也很方便,性能也非常高。
      
       
       
         
       

         
       
      


      
       
       
         
          
         
    1. [b]package [/b]concurrent;
    2. [b]import [/b]java.util.concurrent.ExecutorService;
    3. [b]import [/b]java.util.concurrent.Executors;
    4. [b]public class [/b]TestThreadPool {
    5.    [b]public static [/b][b]void [/b]main(String args[]) [b]throws [/b]InterruptedException {
    6.      // only two threads
    7.      ExecutorService exec = Executors.newFixedThreadPool(2);
    8.      [b]for[/b]([b]int [/b]index = 0; index < 100; index++) {
    9.        Runnable run = [b]new [/b]Runnable() {
    10.          [b]public [/b][b]void [/b]run() {
    11.            [b]long [/b]time = ([b]long[/b]) (Math.random() * 1000);
    12.            System.out.println("Sleeping " + time + "ms");
    13.              [b]try [/b]{
    14.                Thread.sleep(time);
    15.              } [b]catch [/b](InterruptedException e) {
    16.              }
    17.          }
    18.        };
    19.        exec.execute(run);
    20.      }
    21.      // must shutdown
    22.      exec.shutdown();
    23.    }
    24. }
    复制代码

          
         
       
       
          上面是一个简单的例子,使用了2个大小的线程池来处理100个线程。但有一个问题:在for循环的过程中,会等待线程池有空闲的线程,所以主线程会阻塞的。 为了解决这个问题,一般启动一个线程来做for循环,就是为了避免由于线程池满了造成主线程阻塞。不过在这里我没有这样处理。[重要修正:经过测试,即使 线程池大小小于实际线程数大小,线程池也不会阻塞的,这与Tomcat的线程池不同,它将Runnable实例放到一个“无限”的 BlockingQueue中,所以就不用一个线程启动for循环,Doug Lea果然厉害]
      另外它使用了Executors的静态函数生成一个固定的线程池,顾名思义,线程池的线程是不会释放的,即使它是Idle。这就会产生性能问题,比如如果线 程池的大小为200,当全部使用完毕后,所有的线程会继续留在池中,相应的内存和线程切换(while(true)+sleep循环)都会增加。如果要避 免这个问题,就必须直接使用ThreadPoolExecutor()来构造。可以像Tomcat的线程池一样设置“最大线程数”、“最小线程数”和“空 闲线程keepAlive的时间”。通过这些可以基本上替换Tomcat的线程池实现方案。
      需要注意的是线程池必须使用shutdown来显式关闭,否则主线程就无法退出。shutdown也不会阻塞主线程。
      许多 长时间运行的应用有时候需要定时运行任务完成一些诸如统计、优化等工作,比如在电信行业中处理用户话单时,需要每隔1分钟处理话单;网站每天凌晨统计用户 访问量、用户数;大型超时凌晨3点统计当天销售额、以及最热卖的商品;每周日进行数据库备份;公司每个月的10号计算工资并进行转帐等,这些都是定时任 务。通过 java的并发库concurrent可以轻松的完成这些任务,而且非常的简单。
       
       
         
          
          
          
    1. [b]package [/b]concurrent;
    2. [b]import static [/b]java.util.concurrent.TimeUnit.SECONDS;
    3. [b]import [/b]java.util.Date;
    4. [b]import [/b]java.util.concurrent.Executors;
    5. [b]import [/b]java.util.concurrent.ScheduledExecutorService;
    6. [b]import [/b]java.util.concurrent.ScheduledFuture;
    7. [b]public class [/b]TestScheduledThread {
    8.    [b]public static [/b][b]void [/b]main(String[] args) {
    9.      [b]final [/b]ScheduledExecutorService scheduler = Executors
    10.          .newScheduledThreadPool(2);
    11.      [b]final [/b]Runnable beeper = [b]new [/b]Runnable() {
    12.        [b]int [/b]count = 0;
    13.        [b]public [/b][b]void [/b]run() {
    14.          System.out.println([b]new [/b]Date() + " beep " + (++count));
    15.        }
    16.      };
    17.      // 1秒钟后运行,并每隔2秒运行一次
    18.      [b]final [/b]ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(
    19.          beeper, 1, 2, SECONDS);
    20.      // 2秒钟后运行,并每次在上次任务运行完后等待5秒后重新运行
    21.      [b]final [/b]ScheduledFuture<?> beeperHandle2 = scheduler
    22.          .scheduleWithFixedDelay(beeper, 2, 5, SECONDS);
    23.      // 30秒后结束关闭任务,并且关闭Scheduler
    24.      scheduler.schedule([b]new [/b]Runnable() {
    25.        [b]public [/b][b]void [/b]run() {
    26.          beeperHandle.cancel([b]true[/b]);
    27.          beeperHandle2.cancel([b]true[/b]);
    28.          scheduler.shutdown();
    29.        }
    30.      }, 30, SECONDS);
    31.    }
    32. }
    复制代码

          
          
         
       
       为了退出进程,上面的代码中加入了关闭Scheduler的操作。而对于24小时运行的应用而言,是没有必要关闭Scheduler的。
       
      
         在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,往往会等待其他线程都完成某一阶段后再执行,等所有线程都到达某一个阶段后再统一执行。
         比如有几个旅行团需要途经深圳、广州、韶关、长沙最后到达武汉。旅行团中有自驾游的,有徒步的,有乘坐旅游大巴的;这些旅行团同时出发,并且每到一个目的地,都要等待其他旅行团到达此地后再同时出发,直到都到达终点站武汉。
    这时候CyclicBarrier就可以派上用场。CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。
      
       
       
         
       
    1. [b]package [/b]concurrent;
    2. [b]import [/b]java.text.SimpleDateFormat;
    3. [b]import [/b]java.util.Date;
    4. [b]import [/b]java.util.concurrent.BrokenBarrierException;
    5. [b]import [/b]java.util.concurrent.CyclicBarrier;
    6. [b]import [/b]java.util.concurrent.ExecutorService;
    7. [b]import [/b]java.util.concurrent.Executors;
    8. [b]public class [/b]TestCyclicBarrier {
    9.    // 徒步需要的时间: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhan
    10.    [b]private static [/b][b]int[/b][] timeWalk = { 5, 8, 15, 15, 10 };
    11.    // 自驾游
    12.    [b]private static [/b][b]int[/b][] timeSelf = { 1, 3, 4, 4, 5 };
    13.    // 旅游大巴
    14.    [b]private static [/b][b]int[/b][] timeBus = { 2, 4, 6, 6, 7 };
    15.    
    16.    [b]static [/b]String now() {
    17.      SimpleDateFormat sdf = [b]new [/b]SimpleDateFormat("HH:mm:ss");
    18.      [b]return [/b]sdf.format([b]new [/b]Date()) + ": ";
    19.    }
    20.    [b]static class [/b]Tour [b]implements [/b]Runnable {
    21.      [b]private [/b][b]int[/b][] times;
    22.      [b]private [/b]CyclicBarrier barrier;
    23.      [b]private [/b]String tourName;
    24.      [b]public [/b]Tour(CyclicBarrier barrier, String tourName, [b]int[/b][] times) {
    25.        [b]this[/b].times = times;
    26.        [b]this[/b].tourName = tourName;
    27.        [b]this[/b].barrier = barrier;
    28.      }
    29.      [b]public [/b][b]void [/b]run() {
    30.        [b]try [/b]{
    31.          Thread.sleep(times[0] * 1000);
    32.          System.out.println(now() + tourName + " Reached Shenzhen");
    33.          barrier.await();
    34.          Thread.sleep(times[1] * 1000);
    35.          System.out.println(now() + tourName + " Reached Guangzhou");
    36.          barrier.await();
    37.          Thread.sleep(times[2] * 1000);
    38.          System.out.println(now() + tourName + " Reached Shaoguan");
    39.          barrier.await();
    40.          Thread.sleep(times[3] * 1000);
    41.          System.out.println(now() + tourName + " Reached Changsha");
    42.          barrier.await();
    43.          Thread.sleep(times[4] * 1000);
    44.          System.out.println(now() + tourName + " Reached Wuhan");
    45.          barrier.await();
    46.        } [b]catch [/b](InterruptedException e) {
    47.        } [b]catch [/b](BrokenBarrierException e) {
    48.        }
    49.      }
    50.    }
    51.    [b]public static [/b][b]void [/b]main(String[] args) {
    52.      // 三个旅行团
    53.      CyclicBarrier barrier = [b]new [/b]CyclicBarrier(3);
    54.      ExecutorService exec = Executors.newFixedThreadPool(3);
    55.      exec.submit([b]new [/b]Tour(barrier, "WalkTour", timeWalk));
    56.      exec.submit([b]new [/b]Tour(barrier, "SelfTour", timeSelf));
    57.      exec.submit([b]new [/b]Tour(barrier, "BusTour", timeBus));
    58.      exec.shutdown();
    59.    }
    60. }
    复制代码

         
       
       
      
    运行结果:
    00:02:25: SelfTour Reached Shenzhen
    00:02:25: BusTour Reached Shenzhen
    00:02:27: WalkTour Reached Shenzhen
    00:02:30: SelfTour Reached Guangzhou
    00:02:31: BusTour Reached Guangzhou
    00:02:35: WalkTour Reached Guangzhou
    00:02:39: SelfTour Reached Shaoguan
    00:02:41: BusTour Reached Shaoguan
         并发库中的BlockingQueue是一个比较好玩的类,顾名思义,就是阻塞队列。该类主要提供了两个方法put()和take(),前者将一个对象放到队列中,如果队列已经满了,就等待直到有空闲节点;后者从head取一个对象,如果没有对象,就等待直到有可取的对象。
    下 面的例子比较简单,一个读线程,用于将要处理的文件对象添加到阻塞队列中,另外四个写线程用于取出文件对象,为了模拟写操作耗时长的特点,特让线程睡眠一 段随机长度的时间。另外,该Demo也使用到了线程池和原子整型(AtomicInteger),AtomicInteger可以在并发情况下达到原子化 更新,避免使用了synchronized,而且性能非常高。由于阻塞队列的put和take操作会阻塞,为了使线程退出,特在队列中添加了一个“标 识”,算法中也叫“哨兵”,当发现这个哨兵后,写线程就退出。
    当然线程池也要显式退出了。
    1. [b]package [/b]concurrent;
    2. [b]import [/b]java.io.File;
    3. [b]import [/b]java.io.FileFilter;
    4. [b]import [/b]java.util.concurrent.BlockingQueue;
    5. [b]import [/b]java.util.concurrent.ExecutorService;
    6. [b]import [/b]java.util.concurrent.Executors;
    7. [b]import [/b]java.util.concurrent.LinkedBlockingQueue;
    8. [b]import [/b]java.util.concurrent.atomic.AtomicInteger;
    9. [b]public class [/b]TestBlockingQueue {
    10.    [b]static [/b][b]long [/b]randomTime() {
    11.      [b]return [/b]([b]long[/b]) (Math.random() * 1000);
    12.    }
    13.    [b]public static [/b][b]void [/b]main(String[] args) {
    14.      // 能容纳100个文件
    15.      [b]final [/b]BlockingQueue<File> queue = [b]new [/b]LinkedBlockingQueue<File>(100);
    16.      // 线程池
    17.      [b]final [/b]ExecutorService exec = Executors.newFixedThreadPool(5);
    18.      [b]final [/b]File root = [b]new [/b]File("F:\JavaLib");
    19.      // 完成标志
    20.      [b]final [/b]File exitFile = [b]new [/b]File("");
    21.      // 读个数
    22.      [b]final [/b]AtomicInteger rc = [b]new [/b]AtomicInteger();
    23.      // 写个数
    24.      [b]final [/b]AtomicInteger wc = [b]new [/b]AtomicInteger();
    25.      // 读线程
    26.      Runnable read = [b]new [/b]Runnable() {
    27.        [b]public [/b][b]void [/b]run() {
    28.          scanFile(root);
    29.          scanFile(exitFile);
    30.        }
    31.        [b]public [/b][b]void [/b]scanFile(File file) {
    32.          [b]if [/b](file.isDirectory()) {
    33.            File[] files = file.listFiles([b]new [/b]FileFilter() {
    34.              [b]public [/b][b]boolean [/b]accept(File pathname) {
    35.                [b]return [/b]pathname.isDirectory()
    36.                    || pathname.getPath().endsWith(".java");
    37.              }
    38.            });
    39.            [b]for [/b](File one : files)
    40.              scanFile(one);
    41.          } [b]else [/b]{
    42.            [b]try [/b]{
    43.              [b]int [/b]index = rc.incrementAndGet();
    44.              System.out.println("Read0: " + index + " "
    45.                  + file.getPath());
    46.              queue.put(file);
    47.            } [b]catch [/b](InterruptedException e) {
    48.            }
    49.          }
    50.        }
    51.      };
    52.      exec.submit(read);
    53.      // 四个写线程
    54.      [b]for [/b]([b]int [/b]index = 0; index < 4; index++) {
    55.        // write thread
    56.        [b]final [/b][b]int [/b]NO = index;
    57.        Runnable write = [b]new [/b]Runnable() {
    58.          String threadName = "Write" + NO;
    59.          [b]public [/b][b]void [/b]run() {
    60.            [b]while [/b]([b]true[/b]) {
    61.              [b]try [/b]{
    62.                Thread.sleep(randomTime());
    63.                [b]int [/b]index = wc.incrementAndGet();
    64.                File file = queue.take();
    65.                // 队列已经无对象
    66.                [b]if [/b](file == exitFile) {
    67.                  // 再次添加"标志",以让其他线程正常退出
    68.                  queue.put(exitFile);
    69.                  [b]break[/b];
    70.                }
    71.                System.out.println(threadName + ": " + index + " "
    72.                    + file.getPath());
    73.              } [b]catch [/b](InterruptedException e) {
    74.              }
    75.            }
    76.          }
    77.        };
    78.        exec.submit(write);
    79.      }
    80.      exec.shutdown();
    81.    }
    82. }
    复制代码

    1. [/code]
    2. [code]
    复制代码

      
      从名字可以看出,CountDownLatch是一个倒数计数的锁,当倒数到0时触发事件,也就是开锁,其他人就可以进入了。在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;同时当线程都完成后也会触发事件,以便进行后面的操作。
      CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。
      一个CountDouwnLatch实例是不能重复使用的,也就是说它是一次性的,锁一经被打开就不能再关闭使用了,如果想重复使用,请考虑使用CyclicBarrier
      下面的例子简单的说明了CountDownLatch的使用方法,模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。
      同样,线程池需要显式shutdown。
       
       
         
          
         
    1. [b]package [/b]concurrent;
    2. [b]import [/b]java.util.concurrent.CountDownLatch;
    3. [b]import [/b]java.util.concurrent.ExecutorService;
    4. [b]import [/b]java.util.concurrent.Executors;
    5. [b]public class [/b]TestCountDownLatch {
    6.    [b]public static [/b][b]void [/b]main(String[] args) [b]throws [/b]InterruptedException {
    7.      // 开始的倒数锁
    8.      [b]final [/b]CountDownLatch begin = [b]new [/b]CountDownLatch(1);
    9.      // 结束的倒数锁
    10.      [b]final [/b]CountDownLatch end = [b]new [/b]CountDownLatch(10);
    11.      // 十名选手
    12.      [b]final [/b]ExecutorService exec = Executors.newFixedThreadPool(10);
    13.      [b]for[/b]([b]int [/b]index = 0; index < 10; index++) {
    14.        [b]final [/b][b]int [/b]NO = index + 1;
    15.        Runnable run = [b]new [/b]Runnable(){
    16.          [b]public [/b][b]void [/b]run() {
    17.            [b]try [/b]{
    18.              begin.await();
    19.              Thread.sleep(([b]long[/b]) (Math.random() * 10000));
    20.              System.out.println("No." + NO + " arrived");
    21.            } [b]catch [/b](InterruptedException e) {
    22.            } [b]finally [/b]{
    23.              end.countDown();
    24.            }
    25.          }
    26.        };
    27.        exec.submit(run);
    28.      }
    29.      System.out.println("Game Start");
    30.      begin.countDown();
    31.      end.await();
    32.      System.out.println("Game Over");
    33.      exec.shutdown();
    34.    }
    35. }
    复制代码

          
         
       
       
      运行结果:
    Game Start
    No.4 arrived
    No.1 arrived
    No.7 arrived
    No.9 arrived
    No.3 arrived
    No.2 arrived
    No.8 arrived
    No.10 arrived
    No.6 arrived
    No.5 arrived
    Game Over  
      有时 候在实际应用中,某些操作很耗时,但又不是不可或缺的步骤。比如用网页浏览器浏览新闻时,最重要的是要显示文字内容,至于与新闻相匹配的图片就没有那么重 要的,所以此时首先保证文字信息先显示,而图片信息会后显示,但又不能不显示,由于下载图片是一个耗时的操作,所以必须一开始就得下载。
       
       Java的并发库Future类 就可以满足这个要求。Future的重要方法包括get()和cancel(),get()获取数据对象,如果数据没有加载,就会阻塞直到取到数据,而 cancel()是取消数据加载。另外一个get(timeout)操作,表示如果在timeout时间内没有取到就失败返回,而不再阻塞。
       下面的Demo简单的说明了Future的使用方法:一个非常耗时的操作必须一开始启动,但又不能一直等待;其他重要的事情又必须做,等完成后,就可以做不重要的事情。
       
         
          
          
            
          
    1. [b]package [/b]concurrent;
    2. [b]import [/b]java.util.concurrent.Callable;
    3. [b]import [/b]java.util.concurrent.ExecutionException;
    4. [b]import [/b]java.util.concurrent.ExecutorService;
    5. [b]import [/b]java.util.concurrent.Executors;
    6. [b]import [/b]java.util.concurrent.Future;
    7. [b]public class [/b]TestFutureTask {
    8.    [b]public static [/b][b]void [/b]main(String[] args)[b]throws [/b]InterruptedException,
    9.        ExecutionException {
    10.      [b]final [/b]ExecutorService exec = Executors.newFixedThreadPool(5);
    11.      Callable<String> call = [b]new [/b]Callable<String>() {
    12.        [b]public [/b]String call() [b]throws [/b]Exception {
    13.          Thread.sleep(1000 * 5);
    14.          [b]return [/b]"Other less important but longtime things.";
    15.        }
    16.      };
    17.      Future<String> task = exec.submit(call);
    18.      // 重要的事情
    19.      Thread.sleep(1000 * 3);
    20.      System.out.println("Let"s do important things.");
    21.      // 其他不重要的事情
    22.      String obj = task.get();
    23.      System.out.println(obj);
    24.      // 关闭线程池
    25.      exec.shutdown();
    26.    }
    27. }
    复制代码

          
          
         
       
       运行结果:
    Let"s do important things.
    Other less important but longtime things.
         
       
        考虑以下场景:浏览网页时,浏览器了5个线程下载网页中的图片文件,由于图片大小、网站访问速度等诸多因素的影响,完成图片下载的时间就会有很大的不同。如果先下载完成的图片就会被先显示到界面上,反之,后下载的图片就后显示。
        Java的并发库CompletionService可 以满足这种场景要求。该接口有两个重要方法:submit()和take()。submit用于提交一个runnable或者callable,一般会提 交给一个线程池处理;而take就是取出已经执行完毕runnable或者callable实例的Future对象,如果没有满足要求的,就等待了。 CompletionService还有一个对应的方法poll,该方法与take类似,只是不会等待,如果没有满足要求,就返回null对象。
         
          
          
            
          
    1. [b]package [/b]concurrent;
    2. [b]import [/b]java.util.concurrent.Callable;
    3. [b]import [/b]java.util.concurrent.CompletionService;
    4. [b]import [/b]java.util.concurrent.ExecutionException;
    5. [b]import [/b]java.util.concurrent.ExecutorCompletionService;
    6. [b]import [/b]java.util.concurrent.ExecutorService;
    7. [b]import [/b]java.util.concurrent.Executors;
    8. [b]import [/b]java.util.concurrent.Future;
    9. [b]public class [/b]TestCompletionService {
    10.    [b]public static [/b][b]void [/b]main(String[] args) [b]throws [/b]InterruptedException,
    11.        ExecutionException {
    12.      ExecutorService exec = Executors.newFixedThreadPool(10);
    13.      CompletionService<String> serv =
    14.        [b]new [/b]ExecutorCompletionService<String>(exec);
    15.      [b]for [/b]([b]int [/b]index = 0; index < 5; index++) {
    16.        [b]final [/b][b]int [/b]NO = index;
    17.        Callable<String> downImg = [b]new [/b]Callable<String>() {
    18.          [b]public [/b]String call() [b]throws [/b]Exception {
    19.            Thread.sleep(([b]long[/b]) (Math.random() * 10000));
    20.            [b]return [/b]"Downloaded Image " + NO;
    21.          }
    22.        };
    23.        serv.submit(downImg);
    24.      }
    25.      Thread.sleep(1000 * 2);
    26.      System.out.println("Show web content");
    27.      [b]for [/b]([b]int [/b]index = 0; index < 5; index++) {
    28.        Future<String> task = serv.take();
    29.        String img = task.get();
    30.        System.out.println(img);
    31.      }
    32.      System.out.println("End");
    33.      // 关闭线程池
    34.      exec.shutdown();
    35.    }
    36. }
    复制代码

            
          
          
         
        运行结果:
    Show web content
    Downloaded Image 1
    Downloaded Image 2
    Downloaded Image 4
    Downloaded Image 0
    Downloaded Image 3
    End
          
        操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java并发库Semaphore可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,acquire()获取一个许可,如果没有就等待,而release()释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。  
        Semaphore维护了当前访问的个数,提供同步机制,控制同时访问的个数。在数据结构中链表可以保存“无限”的节点,用Semaphore可以实现有限大小的链表。另外重入锁ReentrantLock也可以实现该功能,但实现上要负责些,代码也要复杂些。
        下面的Demo中申明了一个只有5个许可的Semaphore,而有20个线程要访问这个资源,通过acquire()和release()获取和释放访问许可。
         
          
          
            
          
    1. [b]package [/b]concurrent;
    2. [b]import [/b]java.util.concurrent.ExecutorService;
    3. [b]import [/b]java.util.concurrent.Executors;
    4. [b]import [/b]java.util.concurrent.Semaphore;
    5. [b]public class [/b]TestSemaphore {
    6.    [b]public static [/b][b]void [/b]main(String[] args) {
    7.      // 线程池
    8.      ExecutorService exec = Executors.newCachedThreadPool();
    9.      // 只能5个线程同时访问
    10.      [b]final [/b]Semaphore semp = [b]new [/b]Semaphore(5);
    11.      // 模拟20个客户端访问
    12.      [b]for [/b]([b]int [/b]index = 0; index < 20; index++) {
    13.        [b]final [/b][b]int [/b]NO = index;
    14.        Runnable run = [b]new [/b]Runnable() {
    15.          [b]public [/b][b]void [/b]run() {
    16.            [b]try [/b]{
    17.              // 获取许可
    18.              semp.acquire();
    19.              System.out.println("Accessing: " + NO);
    20.              Thread.sleep(([b]long[/b]) (Math.random() * 10000));
    21.              // 访问完后,释放
    22.              semp.release();
    23.            } [b]catch [/b](InterruptedException e) {
    24.            }
    25.          }
    26.        };
    27.        exec.execute(run);
    28.      }
    29.      // 退出线程池
    30.      exec.shutdown();
    31.    }
    32. }
    复制代码

            
          
          
         
        运行结果:
    Accessing: 0
    Accessing: 1
    Accessing: 2
    Accessing: 3
    Accessing: 4
    Accessing: 5
    Accessing: 6
    Accessing: 7
    Accessing: 8
    Accessing: 9
    Accessing: 10
    Accessing: 11
    Accessing: 12
    Accessing: 13
    Accessing: 14
    Accessing: 15
    Accessing: 16
    Accessing: 17
    Accessing: 18
    Accessing: 19
       
       
      
      



      
      
       
       

         
       

         
       
      
    复制代码

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

    使用道具 举报

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

    本版积分规则

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

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

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

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