Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1814559
  • 博文数量: 438
  • 博客积分: 9799
  • 博客等级: 中将
  • 技术积分: 6092
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-25 17:25
文章分类

全部博文(438)

文章存档

2019年(1)

2013年(8)

2012年(429)

分类: Java

2012-03-26 01:10:59

java里的Thread可以启动一个Runnable


  1. class MyRunnable implements Runnable {
  2.     public void run() {
  3.         System.out.println("OK");
  4.     }
  5. }

  6. void testRunnable() {
  7.     Thread t = new Thread(new MyRunnable());
  8.     t.start();
  9. }

Thread本身也实现了Runnable
  1. class MyThread extends Thread {
  2.     public void run() {
  3.         System.out.println("OK");
  4.     }
  5. }

  6. void testThread() {
  7.     Thread t = new MyThread();
  8.     t.start();
  9. }

你可以设置Thread的优先级
  1. Thread t1 = new MyThread();
  2. t1.setPriority(Thread.MAX_PRIORITY);
  3. Thread t2 = new MyThread();
  4. t2.setPriority(Thread.NORM_PRIORITY);
  5. Thread t3 = new MyThread();
  6. t3.setPriority(Thread.MIN_PRIORITY);

也可以把一个线程设为后台线程(daemon)
  1. Thread t = new MyThread();
  2. t.setDaemon(true);
  3. t.start();

一个程序会等待所有非后台线程运行结束后才会退出,而后台线程会在所有非后台线程运行结束后立即终止。

调用Thread.join()方法可以等待某线程的结束:


  1. Thread t = new MyThread();
  2. t.start();
  3. t.join();
  4. System.out.println("Thread quits");


线程可以通过yield来放弃当前的时间片,通过sleep来睡眠一段时间:


  1. class MyRunnable implements Runnable {
  2.     public void run() {
  3.         try {
  4.             for (int n = 0; n < 1000000; ++n) {
  5.                 if (n % 50000 == 0)
  6.                     Thread.yield();
  7.                 else if (n % 20000 == 0)
  8.                     Thread.sleep(500);
  9.                 else if (n % 1000 == 0)
  10.                     TimeUnit.MICROSECONDS.sleep(200);
  11.             }
  12.         } catch (InterruptedException e) {}
  13.     }
  14. }


使用Executors来使用线程池:


  1. ExecutorService es = Executors.newCachedThreadPool();
  2. for (int i = 0; i < 5; ++i)
  3.     es.execute(new MyRunnable());

上面的代码等价于创建了5个Thread并调用它们的start。你可以提供工厂类(ThreadFactory)来定制创建的Thread:
  1. ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {
  2.     @Override
  3.     public Thread newThread(Runnable r) {
  4.         Thread t = new Thread();
  5.         t.setDaemon(true);
  6.         return t;
  7.     }
  8. });
  9. es.execute(new MyRunnable());

cachedThreadPool可以根据需要创建线程,当一个线程结束时并不释放它而是把它回收到池里,以便下次使用。而fixedThreadPool一次性创建固定数量的线程,如果某任务需要线程时而池里没有剩余线程时,则该任务要等待直接某个线程运行结束才能获得线程。给出一个例子:
  1. void testFixedThreadPool() {
  2.     class InnerRunnable implements Runnable {
  3.         InnerRunnable(int id) {
  4.             this.id = id;
  5.         }

  6.         public void run() {
  7.             for (int i = 0; i < 10; ++i)
  8.                 try {
  9.                     Thread.sleep(500);
  10.                 } catch (InterruptedException e) {}
  11.             System.out.println("thread-" + id + " ends");
  12.         }

  13.         private int id;
  14.     }

  15.     ExecutorService es = Executors.newFixedThreadPool(5);
  16.     for (int i = 0; i < 10; ++i)
  17.         es.execute(new InnerRunnable(i));
  18. }

另一种线程池是singleThreadPool,它等同于线程数为1的FixedThreadPool。你可以用Executors.newSingleThreadExecutor()来创建。


Runnable不能返回值,如果需要返回值,使用Callable,并通过Executors.submit来启动线程:


  1. class MyThread extends Thread {
  2.     public void run() {
  3.         System.out.println("OK");
  4.     }
  5. }

  6. void testCallable() throws InterruptedException, ExecutionException {
  7.     ExecutorService es = Executors.newCachedThreadPool();
  8.     Future<Integer> f = es.submit(new MyCallable());
  9.     int i = f.get();
  10.     System.out.println(i); // 10
  11. }


线程里的异常是不能被其它线程捕获的。比如下面的代码是不能工作的:


  1. void testThreadException() {
  2.     try {
  3.         new Thread() {
  4.             public void run() {
  5.                 throw new RuntimeException();
  6.             }
  7.         }.start();
  8.     } catch (RuntimeException re) {
  9.         System.out.println("never be called!!");
  10.     }
  11. }

如果要捕获线程抛出的异常,调用Thread.setUncaughtExceptionHandler
  1. void testUncaughtException() {
  2.     Thread t = new Thread() {
  3.         public void run() {
  4.             throw new RuntimeException();
  5.         }
  6.     };
  7.     t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
  8.         public void uncaughtException(Thread t, Throwable e) {
  9.             System.out.println("Catch the exception!!");
  10.         }
  11.     });
  12.     t.start();
  13. }


为了制造出线程间的共享资源的情况,下面给出一个例子:


  1. class MyClass{
  2.     void print(int id) {
  3.         for (int i = 0; i < 5; ++i)
  4.         {
  5.             System.out.print(id + " ");
  6.             Thread.yield();
  7.         }
  8.     }
  9.     
  10.     class MyThread extends Thread {
  11.         MyThread(int id) { this.id = id; }
  12.         public void run() {
  13.             print(id);
  14.         }
  15.     
  16.         int id;
  17.     }
  18.     
  19.     static void testSynchronized() {
  20.         MyClass mc = new MyClass();
  21.         for (int i = 0; i < 5; ++i) {
  22.             mc.new MyThread(i).start();
  23.         }
  24.     }
  25. }

上面的5个线程共享同一个MyClass对象,并调用它的testSynchronized方法。该方法会输出类似于以下的字符串:1 3 0 2 4 0 2 4 0 2 4 0 2 4 0 2 4 1 1 1 1 3 3 3 3 ,可见print方法被其它线程中断。为了同步print方法,可以用synchroized关键字修饰上面的print方法:
  1. synchronized void print(int id) {...}

这样输出的字符串会类似于:0 0 0 0 0 4 4 4 4 4 2 2 2 2 2 3 3 3 3 3 1 1 1 1 1 ,说明print已经成了一个原子操作。其实synchronized等价于给整个方法加了一个锁,我们完全可以用锁来实现:
  1. class MyClass {
  2.     Lock l = new ReentrantLock();    
  3.     void print_lock(int id) {
  4.         l.lock();
  5.         for (int i = 0; i < 5; ++i)
  6.         {
  7.             System.out.print(id + " ");
  8.             Thread.yield();
  9.         }
  10.         l.unlock();
  11.     }
  12. }

我们可以用trylock来防止阻塞:
  1. void print_trylock(int id) {
  2.     try {
  3.         boolean b = l.tryLock(300, TimeUnit.MILLISECONDS);
  4.         if (b) {
  5.             for (int i = 0; i < 5; ++i)
  6.             {
  7.                 System.out.print(id + " ");
  8.                 Thread.yield();
  9.             }
  10.             l.unlock();
  11.         }
  12.     } catch (InterruptedException e) {}
  13. }

很明显还是synchroized来得简单些。synchronized也可以同步某一段代码,而不是整个方法:
  1. void print_synchronized_part(int id) {
  2.     synchronized(this) {
  3.         for (int i = 0; i < 5; ++i)
  4.         {
  5.             System.out.print(id + " ");
  6.             Thread.yield();
  7.         }
  8.     }
  9. }

synchronized可以接受一个Object作为参数,要注意需要同步的参数必须是同一个对象,比如两个方法f() { synchronized(object_one) {} }和g() { synchronized(object_one){} }是可以同步的,但f() { synchronized(object_one) {} } 和 g() {synchronized(object_two) {} }是不能同步的。

现在我们再来模拟一下多个线程同时对同一资源进行修改的情况。下面的程序让两个线程分别对共享的整型数加N次和减N次,预期的正确结果是当两个线程运行结束后,整型值应该为0:


  1. class MyClass{
  2.     int resource;
  3.     void changeValue(boolean increase) {
  4.         for (int i = 0; i < 600000; ++i)
  5.             if (increase)
  6.                 ++resource;
  7.             else
  8.                 --resource;
  9.     }
  10.     class InnerThread extends Thread {
  11.         public InnerThread(boolean increase) {this.increase = increase;};
  12.         private boolean increase;
  13.         public void run() {
  14.             changeValue(increase);
  15.         }
  16.     }    
  17.     static void testAtomicOperation() throws InterruptedException {
  18.         MyClass mc = new MyClass();
  19.         Thread t1 = mc.new InnerThread(true);
  20.         Thread t2 = mc.new InnerThread(false);
  21.         t1.start();
  22.         t2.start();
  23.         t1.join();
  24.         t2.join();
  25.         
  26.         System.out.println(mc.resource); // might be 321296, but it's supposed to be zero
  27.     }
  28. }

java提供了一个关键字volatile,它的本意是表示某个变量的修改操作是原子操作,不需要同步的,但是它是不工作的。你可以在上面的代码里给resource加入volatile修饰,但得到的结果仍可能不是0。
  1. volatile int resource = 0; // thread unsafe

可以使用AtomicInteger来表示原子操作。修改上例的部份代码:
  1. AtomicInteger resource = new AtomicInteger(0);

  2. void changeValue(boolean increase) {
  3. for (int i = 0; i < 600000; ++i)
  4.     if (increase)
  5.         resource.addAndGet(1);
  6.     else
  7.         resource.addAndGet(-1);
  8. }

你还可以在java.util.concurrent.atomic包里找到其它的原子操作的类,比如AtomicBooleanAtomicLong等。
其实线程冲突的最根本的原因有共享的资源,如果每个线程都有自己的变量,那自然不会有冲突。我们可以用ThreadLocal定义这样的变量:
  1. ThreadLocal<Integer> resource = new ThreadLocal<Integer>() {
  2.     @Override
  3.     protected Integer initialValue() {
  4.         return 0;
  5.     }
  6. };
  7. void changeValue(boolean increase) {
  8.     for (int i = 0; i < 600000; ++i)
  9.         resource.set(resource.get() + 1);
  10.     System.out.println(resource.get()); // each thread should shows 600000
  11. }

注意一定要覆盖ThreadLocal的initialValue方法,否则你的变量(resource)在新线程里会为null。


线程有四种状态:新建(new)、就绪(runnable)、阻塞(blocked)和死亡(dead)。当线程睡眠、等待、申请锁或响应I/O时,可以从就绪状态进入阻塞状态。睡眠和等待是可以被中断的。当被中断时,会得到一个InterruptException。下面中断一个sleep操作:


  1. void testInterruptSleep() throws InterruptedException {
  2.     Thread t = new Thread() {
  3.         public void run() {
  4.             long time = System.currentTimeMillis();
  5.             try {
  6.                 Thread.sleep(100000);                    
  7.             } catch (InterruptedException e) {
  8.                 System.out.println("Interrupt!!");
  9.             } finally {
  10.                 time -= System.currentTimeMillis();
  11.                 System.out.println("Slept for " + Math.abs(time) + " milliseconds");
  12.             }
  13.         }
  14.     };
  15.     t.start();
  16.     Thread.sleep(2000);
  17.     t.interrupt();
  18. }

等待(wait)睡眠(sleep)的区别在于,等待的线程可以被通知(notify)信号(signal)唤醒。同时线程在等待时会放弃它获得的锁,而睡眠不会,所以wait操作必须和同步一起使用。下面用Future对象来中断一个等待阻塞:
  1. void testInterruptWait() throws InterruptedException {
  2.     Runnable r = new Runnable() {
  3.         public void run() {
  4.             long time = System.currentTimeMillis();
  5.             try {
  6.                 synchronized (this) {
  7.                     wait(8888);
  8.                 }
  9.             } catch (InterruptedException e) {
  10.                 System.out.println("Interrupt!!");
  11.             } finally {
  12.                 time -= System.currentTimeMillis();
  13.                 System.out.println("Slept for " + Math.abs(time) + " milliseconds");
  14.             }
  15.         }
  16.     };
  17.     ExecutorService es = Executors.newCachedThreadPool();
  18.     Future<?> f = es.submit(r);
  19.     Thread.sleep(2000);
  20.     f.cancel(true); // interrupt it
  21. }

我们用它去中断一个处于就绪状态的线程
  1. void testInterrupted() throws InterruptedException {
  2.         Runnable r = new Runnable() {
  3.             public void run() {
  4.                 while (!Thread.interrupted()) {
  5.                     System.out.println("I'm still alive");
  6.                     Thread.yield();
  7.                 }
  8.                 System.out.println("interrupted!!");
  9.             }
  10.         };
  11.         ExecutorService es = Executors.newCachedThreadPool();
  12.         Future<?> f = es.submit(r);
  13.         Thread.sleep(2000);
  14.         es.shutdownNow();
  15.     }

线程可以通过判断Thread.interrupted来确定自己是否被中断。ExecutorService.shutdownNow()可以中断线程池里所有的阻塞线程。


下面给个例子看怎么样使用waitnotify


  1. class NotifyTester {
  2.     synchronized void turnOn() throws InterruptedException {
  3.         while (!Thread.interrupted()) {
  4.             while (on)
  5.                 wait();
  6.             on = true;
  7.             System.out.println("turn on!!");
  8.             notify();
  9.         }        
  10.     }
  11.     
  12.     synchronized void turnOff() throws InterruptedException {
  13.         while (!Thread.interrupted()) {
  14.             while (!on)
  15.                 wait();
  16.             on = false;
  17.             System.out.println("turn off!!");
  18.             notify();
  19.         }
  20.     }
  21.     
  22.     class TurnOnThread extends Thread {
  23.         public void run() {
  24.             try {
  25.                 turnOn();
  26.             } catch (InterruptedException e) {}
  27.         }
  28.     }
  29.     
  30.     class TurnOffThread extends Thread {
  31.             public void run() {
  32.             try {
  33.                 turnOff();
  34.             } catch (InterruptedException e) {}
  35.         }
  36.     }
  37.     
  38.     static void test() {
  39.         NotifyTester nt = new NotifyTester();
  40.         Thread t_on = nt.new TurnOnThread();
  41.         Thread t_off = nt.new TurnOffThread();
  42.         
  43.         t_on.start();
  44.         t_off.start();
  45.         
  46.         try {
  47.             Thread.sleep(9000);
  48.         } catch (InterruptedException e) {}
  49.         
  50.         t_on.interrupt();
  51.         t_off.interrupt();
  52.     }

  53.     boolean on;
  54. }

notify只是唤醒一个等待的线程,而notifyAll会唤醒所有等待的线程。我们还可以用Lock和Condition来完成同样的工作:
  1. class LockSignalTester {
  2.     private Lock l = new ReentrantLock();
  3.     private Condition c = l.newCondition();
  4.     private boolean on;
  5.     
  6.     void turnOn() {
  7.         while (!Thread.interrupted()) {
  8.             l.lock();
  9.             try {
  10.                 while (on)
  11.                     c.await();
  12.                 on = true;
  13.                 System.out.println("turn on!!");
  14.                 c.signal();
  15.             } catch (InterruptedException ie) {
  16.             } finally {
  17.                 l.unlock();
  18.             }
  19.         }        
  20.     }
  21.     
  22.     void turnOff(){
  23.         while (!Thread.interrupted()) {
  24.             l.lock();
  25.             try {
  26.                 while (!on)
  27.                     c.await();
  28.                 on = false;
  29.                 System.out.println("turn off!!");
  30.                 c.signal();
  31.             } catch (InterruptedException ie) {
  32.             } finally {
  33.                 l.unlock();
  34.             }
  35.         }
  36.     }
  37.     
  38.     class TurnOnThread extends Thread {
  39.         public void run() { turnOn(); }
  40.     }
  41.     
  42.     class TurnOffThread extends Thread {
  43.         public void run() { turnOff(); }
  44.     }
  45.     
  46.     static void test() {
  47.         NotifyTester nt = new NotifyTester();
  48.         Thread t_on = nt.new TurnOnThread();
  49.         Thread t_off = nt.new TurnOffThread();
  50.         
  51.         t_on.start();
  52.         t_off.start();
  53.         
  54.         try {
  55.             Thread.sleep(9000);
  56.         } catch (InterruptedException e) {}
  57.         
  58.         t_on.interrupt();
  59.         t_off.interrupt();
  60.     }
  61. }

Condition.awaitCondition.signal/signalAllObject.waitObject.notify/notifyAll等价。


CountDownLatch可以用来同步多个线程。它会有一个初始数,每次调用countDown的时候数值减一但不阻塞,而调用await的时候会阻塞直到数值被减为0为止:


  1. class CountDownLatchTester {
  2.     int counter = 0;
  3.     CountDownLatch cdl = new CountDownLatch(5);
  4.     
  5.     class IncreaseThread extends Thread {
  6.         public void run() {
  7.             try {
  8.                 Thread.sleep(new Random().nextInt(10) * 1000);
  9.             } catch (InterruptedException e1) {}
  10.             synchronized (this) {
  11.                 ++counter;
  12.             }
  13.             try {
  14.                 cdl.countDown();
  15.                 cdl.await();
  16.             } catch (InterruptedException e) {}
  17.             System.out.println(counter);
  18.         }
  19.     }
  20.     
  21.     static void test() {
  22.         CountDownLatchTester cdlt = new CountDownLatchTester();
  23.         for (int i = 0; i < 5; i++)
  24.             cdlt.new IncreaseThread().start();
  25.     }
  26. }

上面代码里的5个线程都会睡眠随机的一段时间,之后增加counter的值,再调用countDown来降低CountDownLatch的值,最后调用await来等待CountDownLatch的值变为0,即所有其它的线程全部结束。程序的输出是5个线程同时打印出数字“5”。

CountDownLatch只做一次同步操作,如果计数器减为0后,所有的await调用都会立即返回。CyclicBarrier可以循环地进行同步,当计数器降为0后,会自动设置为初值以便下一次的同步。CyclicBarrier没有countDown操作,它的await会将计数器减一并阻塞,如果计数器减为0所有阻塞在await操作上的线程都会被唤醒:


  1. class CyclicBarrierTester {
  2.     int counter = 0;
  3.     CyclicBarrier cb = new CyclicBarrier(5);
  4.     
  5.     class IncreaseThread extends Thread {
  6.         public void run() {
  7.             for (int i = 0; i < 3; ++i)
  8.             {
  9.                 try {
  10.                     Thread.sleep(new Random().nextInt(10) * 1000);
  11.                 } catch (InterruptedException e1) {}
  12.                     synchronized (this) {
  13.                     ++counter;
  14.                 }
  15.                 try {
  16.                     cb.await();
  17.                 } catch (InterruptedException e) {
  18.                 } catch (BrokenBarrierException e) {
  19.                 }
  20.                 System.out.println(counter);
  21.             }
  22.         }
  23.     }
  24.     
  25.     static void test() {
  26.         CyclicBarrierTester cbt = new CyclicBarrierTester();
  27.         for (int i = 0; i < 5; i++)
  28.             cbt.new IncreaseThread().start();
  29.     }
  30. }

上例我在thread的run方法里添加了一个循环,从运行结果可以看到这些线程确实进行了三次同步。


DelayQueue可以用来存放Delayed对象。Delayed是一个接口,它可以通过getDelay方法来表示有多久到期或者已经过期了多久。DelayQueue是一个优先级队列,优先级更高的Delayed会最先取出,不管它过期的时间如何。在同等优先级的情况下,会取出某个过期的Delayed。如果当前还有元素但没有元素过期,DelayQueue.take就会阻塞。下面给一个例子:


  1. class DelayQueueTester {
  2.     DelayQueue<MyDelayed> dq = new DelayQueue<MyDelayed>();
  3.     long currentTime = 0;
  4.     
  5.     class MyDelayed implements Delayed {
  6.         long start = System.currentTimeMillis();
  7.         int priority = new Random().nextInt(5);
  8.         
  9.         @Override
  10.         public int compareTo(Delayed o) {
  11.             return priority - ((MyDelayed)o).priority;
  12.         }

  13.         @Override
  14.         public long getDelay(TimeUnit unit) {
  15.             return unit.convert(start - currentTime, TimeUnit.MILLISECONDS);
  16.         }
  17.     }
  18.     
  19.     static void test() throws InterruptedException {
  20.         DelayQueueTester dqt = new DelayQueueTester();
  21.         for (int i = 0; i < 10; i++)
  22.         {
  23.             TimeUnit.MILLISECONDS.sleep(200);
  24.             dqt.dq.offer(dqt.new MyDelayed());
  25.         }
  26.         
  27.         Thread.sleep(1000);
  28.         dqt.currentTime = System.currentTimeMillis();
  29.         
  30.         while (dqt.dq.size() > 0) {
  31.             MyDelayed d = dqt.dq.take();
  32.             System.out.println(d.priority + "\t" + d.start);
  33.         }
  34.     }
  35. }

Delay接口的两个方法compareTo和getDelay。前者用于比较优先级,值越小的优先级越高;后者用于返回离到期的时间,正值说明还没到期,负值说明已经过期。从输出上看,优先级高的会先被取出,但是过期时间更长的并不优先取出,它只保证当前可以取出一个过期元素,但不保证取出的顺序。(书上说的可能不对)


PirorityBlockingQueue当队列里没有元素而试图取元素时,会发生阻塞,同时优先级最高(值最小)的元素会最先被取出:


  1. class PriorityBlockingQueueTester {
  2.     PriorityBlockingQueue<Integer> pbq = new PriorityBlockingQueue<Integer>();
  3.     
  4.     class ReadThread extends Thread {
  5.         public void run() {
  6.             for (int i = 0; i < 1000; i++)
  7.                 try {
  8.                     int n = pbq.take();
  9.                     System.out.println(n);
  10.                 } catch (InterruptedException e) {}
  11.         }
  12.     }
  13.     
  14.     class WriteThread extends Thread {
  15.         public void run() {
  16.             for (int i = 0; i < 200; i++)
  17.             {
  18.                 for (int j = 0; j < 5; j++)
  19.                     pbq.offer(10 - j);
  20.                 try {
  21.                     Thread.sleep(1000);
  22.                 } catch (InterruptedException e) {}
  23.             }
  24.         }
  25.     }
  26.     
  27.     static void test() throws InterruptedException {
  28.         PriorityBlockingQueueTester pbqt = new PriorityBlockingQueueTester();
  29.         pbqt.new ReadThread().start();
  30.         pbqt.new WriteThread().start();
  31.     }
  32. }

上面的代码会循环输出“6 7 8 9 10”。DelayQueuePriorityBlockingQueue都实现了接口BlockingQueue,这种队列都是可以阻塞线程的。其它实现了这个接口的类还有ArrayBlockingQueue(队列大小固定)、LinkedBlockingQueueSynchronousBlockingQueue(每个put方法都会阻塞直到一个take被调用,这个队列本身没有容量,一次只能传递一个数据)。在BlockingQueue引入java前,PipedWriter和PipedReader用来在线程间传递数据。


ShceduledThreadPoolExecutor可以让一个线程延后一段时间再启动:


  1. class ScheduledExecutorTester {
  2.     static class MyRunnable implements Runnable {
  3.         public void run() {
  4.             System.out.println("Start time: " + new Date(System.currentTimeMillis()));
  5.         }
  6.     }
  7.     
  8.     static void test() {
  9.         ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(10);
  10.         System.out.println("Sheduled time: " + new Date(System.currentTimeMillis()));
  11.         stpe.schedule(new MyRunnable(), 2, TimeUnit.SECONDS);
  12.     }
  13. }

上面的schedule方法只执行一次,而scheduleAtFixedRate方法会在固定的频率启动一个新线程:
  1. stpe.schedule(new MyRunnable(), 2, 3, TimeUnit.SECONDS);

第二个参数是第一次启动的延迟,第三个参数是第一次启动后,每次启动的间隔数。上面一行的意思是2秒后执行Runnable,之后每隔3秒执行新的Runnable。

另一个类似的方法是scheduledWithFixedRate,它与scheduleAtFixedRate的区别在于,它在前一个线程结束后过一段时间间隔,才会启动新的线程。stpe.scheduleWithFixedDelay(new MyRunnable(), 1, 3, TimeUnit.SECONDS);上面这行代码的意思是,第一个线程在1秒后启动,之后在线程结束后,再等3秒启动新线程,如此反复。


信号量(Semaphore)允许多个线程同时访问一个资源(锁只允许一个线程)。用Semaphore.aquire来获得资源访问权,如果获得该资源的线程数已经超出限制的话就阻塞,直到有别的线程调用Semaphore.release来释放该资源。


  1. class SemaphoreTester {
  2.     static Semaphore s = new Semaphore(5, true);
  3.     
  4.     static class MyThread extends Thread {
  5.         int id;
  6.         MyThread(int id) { this.id = id; }
  7.         public void run() {
  8.             try {
  9.                 s.acquire();
  10.                 System.out.println("start - " + id);
  11.                 Thread.sleep(3000);
  12.                 System.out.println("end - " + id);
  13.                 s.release();
  14.             } catch (InterruptedException e) {};
  15.         }
  16.     }
  17.     
  18.     static void test() {
  19.         for (int i = 0; i < 20; ++i)
  20.             new MyThread(i).start();
  21.     }
  22. }


Extranger可以让线程之间交换数据:


  1. class ExchangerTester {
  2.     static Exchanger<Integer> e = new Exchanger<Integer>();
  3.     static class MyThread extends Thread {
  4.         int id;
  5.         int extrangedId;
  6.         MyThread(int id) { this.id = id; }
  7.         public void run() {
  8.             try {
  9.                 extrangedId = e.exchange(id);
  10.             } catch (InterruptedException e) {}
  11.         }
  12.         
  13.         public void print() {
  14.             System.out.println("My id: " + id + " - Exchanged id: " + extrangedId);
  15.         }
  16.     }
  17.     
  18.     static void test() {
  19.         MyThread t1 = new MyThread(1);
  20.         MyThread t2 = new MyThread(2);
  21.         t1.start();
  22.         t2.start();
  23.         
  24.         try {
  25.             t1.join();
  26.             t2.join();
  27.         } catch (InterruptedException e) {}
  28.         
  29.         t1.print(); // My id: 1 - Exchanged id: 2
  30.         t2.print(); // My id: 2 - Exchanged id: 1
  31.     }
  32. }


阅读(1298) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~