JavaSE线程笔记

来源:互联网 发布:数据恢复软件合集 编辑:程序博客网 时间:2024/04/29 21:33
1. interupt()和interrupted()和isInterrupted()和InterruptedException
Thread中的成员方法 interrupt()方法用来请求终止线程。interrupt()方法将线程的中断状态置位。
查询中断标志位状态有两种方法:
用成员方法isInterrupted()。isInterrupted()不改变中断状态位。
Thread类中的静态方法interrupted()可以查询当前线程的中断状态位,不过interrupted()将清除中断状态。
*当线程被阻塞时(调用sleep()或wait()),此时如果调用interrupt()方法,阻塞调用将会被InterruptedException异常中断。
测试1:
public class TestJAVA {
    public static void main (String[] args) throws Exception { 
        System .out. println(Thread .currentThread().isInterrupted());
        Thread .currentThread().interrupt();
        System .out. println(Thread .interrupted());
        System .out. println(Thread .currentThread().isInterrupted());     
    }
}
结果:
false
true
false
测试2:
public class TestJAVA {
    public static void main (String[] args) throws Exception { 
        Thread t1 =new Thread (new t ());
        t1 .start();
        System .out. println(t1 .isInterrupted());
        t1 .interrupt();
        System .out. println(t1 .isInterrupted());
    }
}
class t implements Runnable{
    public void run(){
         try{
        Thread .sleep( 1000);
         }catch(InterruptedException e){
            e .printStackTrace();
         }
         }
}
结果:
false
true
java.lang.InterruptedException : sleep interrupted
       at java.lang.Thread.sleep( Native Method)
       at t.run( TestJAVA.java:17)
       at java.lang.Thread.run(Unknown Source)

处理InterruptedException的两种选择:
1)设置中断状态位
      void   mySubTask(){
               try{ sleep(delay)}
               catch(InterrputedException e){Thread.currentThread().interrupt();}
          }
2) 向调用者抛异常
      void    mySubTask() throws InterruptedException{
               sleep(delay)
          }
两点注意:
1)貌似线程还没启动(start())时,中断状态位永为false。并且当wait()、join()、sleep(long)抛出InterruptedException后,中断标志位将重新被置为false。看如下代码
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              Thread t1 = new Thread(new t());
              System. out.println("启动前中断前 " + t1.isInterrupted());
              t1.interrupt();
              System. out.println("启动前中断后 " + t1.isInterrupted());
              t1.start();
              System. out.println("启动后中断前 " + t1.isInterrupted());
              t1.interrupt();
              System. out.println("启动后中断后 " + t1.isInterrupted());

       }
}

class t implements Runnable {
        public void run() {
               try {
                     Thread. sleep(1000);
              } catch (InterruptedException e) {
                     System. out.println("中断发生时  "
                                  + Thread. currentThread().isInterrupted());
                     e.printStackTrace();
              }
       }
}
结果为
启动前中断前 false
启动前中断后 false
启动后中断前 false
启动后中断后 true
中断发生时  false
java.lang.InterruptedException : sleep interrupted
       at java.lang.Thread.sleep( Native Method)
       at t.run( TestJAVA.java:21)
       at java.lang.Thread.run(Unknown Source)
2)如果在当前线程调用interrupt()方法后,再调用Thread.interrupted()将不会抛出InterruptedException异常
比较下列代码:
public class TestJAVA {
        public static void main(String[] args) throws Exception {     
              Thread d=Thread. currentThread();
              d.interrupt();
              System. out.println(Thread. interrupted());
              Thread. sleep(1000);
       }
}
结果为
true

public class TestJAVA {
        public static void main(String[] args) throws Exception {     
              Thread d=Thread. currentThread();
              d.interrupt();
              System. out.println(d.isInterrupted());
              Thread. sleep(1000);
       }
}
结果为
true
Exception in thread "main" java.lang.InterruptedException: sleep interrupted
       at java.lang.Thread.sleep( Native Method)
       at TestJAVA.main( TestJAVA.java:9)


2.线程的状态
线程的状态有六种 
New(新生)、Runnable(可运行)、Block(被阻塞)、Waiting(等待)、Timed waiting(计时等待)、Terminated(被终止)
public class TestJAVA {
        public static void main(String[] args) throws Exception {
               for(Thread.State i:Thread.State.values()){
                     System. out.println(i);
              }
       }
}
输出:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
1)New
用new操作符创建一个线程时,他的状态为New。
2)Runnable
一旦线程调用start()方法,线程处于Runnable了状态。
在任何给定时刻,一个可运行的线程可能正在运行也可能没有运行。
3)Block
当一个线程试图获取一个内部的对象锁(非java.util.concurrent锁,而是与synchronized有关的对象锁),而该锁被其他线程持有,则该线程进入阻塞状态。
4)Waiting
当线程等待另一个线程通知调度器一个条件时,他自己进入等待状态(比如wait()、join()、await())。
5)Timed Wating
有几个方法有一个超时参数,调用他们到直线诚进入计时等待(sleep(long)、wait(long)、join(long)、tryLock(long)、await(long))。
6)Terminated
有两个原因导致线程被终止{
     run方法正常退出
     一个没有捕获的异常终止了run方法而意外死亡
}

NEW状态、RUNNABLE状态、TERMINATED状态:
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              Thread t= new Thread(new R());
              System. out.println("new后状态:" +t.getState());
              t.start();
              System. out.println("start后状态:" +t.getState());
              Thread. sleep(1);
              System. out.println("run结束后状态:" +t.getState());
       }
}
class R implements Runnable {
        public void run() {
              
       }
}
结果:
new后状态:NEW
start后状态:RUNNABLE
run结束后状态:TERMINATED

BLOCK状态:
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              R r= new R();
              Thread t1= new Thread(r);
              Thread t2= new Thread(r);
              t1.start();
              t2.start();
              System. out.println("synchronized后状态:" +t2.getState());
       }
}
class R implements Runnable {
        public synchronized void run() {
               try{          
                     Thread. sleep(1000);
              } catch(InterruptedException e){
                     e.printStackTrace();
              }
       }
}
结果(可能不是每次都能出现结果,但多试几次应该就能出现):
synchronized后状态:BLOCKED

WAITING状态:
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              R r1= new R();
              Thread t1= new Thread(r1);
              R r2= new R();
              r2.setThread(t1);
              Thread t2=new Thread(r2);
              t1.start();
               t2.start();
              System. out.println("join后状态:" +t2.getState());
       }
}
class R implements Runnable {
       Thread t;
        public synchronized void run() {
               try{          
                      if(t !=null){
                            t.join();
                     }
                      else{
                           Thread. sleep(1000);
                     }
              } catch(InterruptedException e){
                     e.printStackTrace();
              }
       }
        public void setThread(Thread t){
               this.t =t;
       }
}
结果:
join后状态:WAITING

TIMED WAITING状态:
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              R r= new R();
              Thread t1= new Thread(r);
              t1.start();
              System. out.println("sleep后状态:" +t1.getState());
       }
}
class R implements Runnable {
        public synchronized void run() {
               try{          
                     Thread. sleep(1000);
              } catch(InterruptedException e){
                     e.printStackTrace();
              }
       }
}
结果:
sleep后状态:TIMED_WAITING
3.线程优先级
默认情况下,一个线程继承他的父线程的优先级(父线程的优先级必须在new子线程前确定)。
优先级为1-10;MIN_PRIORITY=1,MAX_PRIORITY=10,默认为NORM_PRIORITY=5.
每当调度器决定运行一个线程时,首先会在具有高优先级的线程中进行选择。
Thread.yield()方法导致当前进程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来会被调度。也就是说优先级比当前线程低的线程将不会被调度。
注:yield()使线程进入Runnable状态

4.守护线程
java线程分为用户线程和守护线程(或称服务线程、后台线程)
用户线程或守护线程必须在start()方法前用setDaemon(boolean)设置,默认用户线程(即setDaemon(false))。
java的垃圾回收是典型的的守护线程。
当只剩下守护线程时,虚拟机就退出了。
如果在start()方法后再调用setDaemon(boolean)方法,将抛出IllegalThreadStateException
yield()前
线程状态RUNNABLE
线程状态BLOCKED
yield()后
线程状态BLOCKED
线程状态TERMINATED
线程状态TERMINATED

5.未捕获异常处理器
任何实现了Thread.uncaughtExceptionHandler接口的类都可以成为一个未捕获异常处理器。
Thread.setDefaultUncaughtExceptionHandler()可以为所有县城安装一个默认的处理器。
Thread的成员方法setUncaughtExceptionHandler()可以为某个线程安装一个独立的处理器。
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              Thread. currentThread().setName("main-thread");
              Thread. setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
                      @Override
                      public void uncaughtException(Thread t, Throwable e) {
                            // TODO Auto-generated method stub
                           System. out.println(t.getName()+"has a default handler");
                     }
              });
              Thread t1= new Thread(new R());
              Thread t2= new Thread(new R());
              t1.setName( "t1-thread");
              t2.setName( "t2-thread");
              t2.setUncaughtExceptionHandler( new UncaughtExceptionHandler() {
                      @Override
                      public void uncaughtException(Thread t, Throwable e) {
                            // TODO Auto-generated method stub
                           System. out.println(t.getName()+"has a special handler");
                     }
              });
              t1.start();
              t2.start();
               throw new NullPointerException();
       }
}

class R implements Runnable {
        public void run() {
               throw new NullPointerException();
       }

}
结果:
t1-threadhas a default handler
t2-threadhas a special handler
main-threadhas a default handler
如果不安装默认的处理器,默认的处理器为空。如果不为独立的线程安装处理器,此时的处理器就是该线程的线程组(ThreadGroup)对象。线程组是一个可以统一管理的线程集合,默认情况下,创建的所有线程属于相同的线程组,但也可能建立其他组。jdk1.5引入了更好的特性用于线程集合的操作,不要再自己的程序中使用线程组。

6.进程的同步
java有两种机制防止代码块受并发访问的干扰:ReentrantLock类和synchronized关键字
6.1
ReentrantLock类。此类以及下面的Condition类是在1.5中出现的,在java.util.concurrent包中。
6.1.1
基本用法结构
myLock.lock();
try{
}finally{
myLock.unlock();
}
把解锁操作放在finally子句中是至关重要的。如果在临界区的代码抛出异常,锁必须被释放。否则其他线程将永远阻塞。
调用lock()方法,当线程无法获得该锁时,线程将进入WAITING状态,而不是阻塞状态。
public class TestJAVA {
        public static void main(String[] args) throws Exception {     
              R r= new R();
              Thread t1= new Thread(r);
              Thread t2= new Thread(r);
              t1.start();
              t2.start();
              Thread. sleep(10);
              System. out.println("lock后状态:" +t2.getState());
       }
}
class R implements Runnable {
       Lock lock= new ReentrantLock();
        public  void run() {
               lock.lock();
               try{                       
                           Thread. sleep(1000);  
              } catch(InterruptedException e){
                     e.printStackTrace();
              } finally{
                      lock.unlock();
              }
       }
}
结果:
lock后状态:WAITING

6.1.2
锁是可以重入的线程可以重复的获得已经持有的锁。锁保持一个持有计数器(hold count)来跟踪对lock方法的嵌套调用。
ublic class TestJAVA {
        public static void main(String[] args) throws Exception {
              R r = new R();
              Thread t = new Thread(r);
              t.start();
       }
}
class R implements Runnable {
       ReentrantLock lock = new ReentrantLock();
        public void run() {
               lock.lock();
               try {
                     System. out.println("lock()后当前持有计数器: " + lock.getHoldCount());
                     nested();
              } finally {
                      lock.unlock();
                     System. out.println("unlock()后当前持有计数器: " + lock.getHoldCount());
              }
       }
        public void nested() {
               lock.lock();
               try {
                     System. out.println("lock()后当前持有计数器: " + lock.getHoldCount());
              } finally {
                      lock.unlock();
                     System. out.println("unlock()后当前持有计数器: " + lock.getHoldCount());
              }
       }
}
结果:
lock()后当前持有计数器: 1
lock()后当前持有计数器: 2
unlock()后当前持有计数器: 1
unlock()后当前持有计数器: 0
6.1.3
条件对象     Condition
    一个锁对象可以有一个或多个相关的条件对象。可以用newCondition()方法获得一个条件对象。习惯上给每一个条件对象命名为可以反映它所表达的条件的名字。
    调用Condition的成员方法await(),当前线程被阻塞,并放弃了锁。
    等待获得所的线程和调用await()方法的线程存在本质上的不同。一旦一个线程调用await()方法,它进入该条件的等待集。当锁可用时,该线程不能马上解除阻塞。相反,他处于阻塞状态,直到另一个线程调用同一条件上的signalAll()方法时为止。一旦锁成为可用,他们中的某个将从await()调用返回,获得该锁并从被阻塞的地方继续执行。注意调用signalAll()不会立即激活一个等待线程,它仅仅解除等待线程的阻塞,以便这些线程可以在当前线程退出同步方法之后,通过竞争实现对对象的访问。
    再次获得锁后,线程应该再次测试该条件。此时有可能已经满足条件,也有可能不满足条件。
    通常对await()的调用应该在如下形式的循环体中:
     while(!(ok to procceed)){
          condition.await();
     }
上面说的阻塞应该不是指BLOCK状态,实验表明当调用await()后,线程是WAITING状态。
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              R r = new R();
              Thread t1 = new Thread(r);
              Thread t2 = new Thread(r);
              t1.setName( "t1");
              t2.setName( "t2");
              t1.start();
              t2.start();
              Thread. sleep(20);
              System. out.println("await()后 " +t1.getState());
       }
}
class R implements Runnable {
       ReentrantLock lock = new ReentrantLock();
    Condition condition=lock.newCondition();
        public void run() {
               lock.lock() ;
                      try {
                            if(Thread.currentThread().getName().equals( "t1")){
                                   condition.await();
                           }
                            else{
                                  Thread. sleep(1000);
                           }
                     } catch (InterruptedException e) {
                           e.printStackTrace();
                     } finally {
                            condition.signalAll();
                            lock.unlock();
                     }      
       }
}
结果:
await()后 WAITING

几点注意:
一、当一个线程调用await()时,它没有办法重新激活自身。程序中的线程不正确地使用await()方法,有可能导致死锁现象。
二、Condition有一个方法signal,是随机解除等待集中某个线程的阻塞状态。这虽然更为有效,但当随机选择的线程发现自己仍不能运行                          时,将再次被阻塞。如果没有其他线程再次调用signal或signalAll,那系统就死锁了。     
三、当一个线程拥有某个条件的锁时,它仅仅可以在该条件上调用await(),signalAl()或signal()方法。
四、必须先获得锁对象,才能调用await或者signalAll或者signal。
   即必需 Lock.lock()-> Condition.await()/Condition.signal()/Condition.signalAll() -> Lock.unlock()
   不然会抛出java.lang.IllegalMonitorStateException
   也就是说调用这三个方法的线程必须是锁的持有者。

小结下锁和条件:
一、锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码。
二、锁可以管理试图进入被保护代码段的线程。
三、锁可以拥有一个或多个相关的条件对象。
四、每个条件对象管理那些已经进入被保护的代码段但还不不能运行的线程。

6.2 synchronized关键字
如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。
public synchronized void method(){ body}  等价于 
public void method(){
      this.intrinsicLock.lock();
      try{
            body
      }finally{
            this.instrinsic.unlock();
      }
} 
内部对像只有一个相关条件。wait()方法添加一个线程到等待集中,notifyAll()、notify()方法解除等待线程的阻塞状态。
也就是说,wait()等价于intrinsicCondition.await();
               notifyAll()等价于instrinsicCondition.signalAll();
将静态方法声明为synchronized也是合法的。如果调用这种方法,该方法获得相关的类对象的内部锁。
e.g 如果Bank类有一个静态的同步方法,那么当该方法被调用时,Bank.class对象的锁被锁住。

内部锁和条件存在一些局限:
不能中断一个正在试图获得锁的线程。
试图获得锁时不能设定超时。
每个锁仅有单一的条件,可能是不够的。

*注:Thread.yield()不释放锁
但Thread的成员方法join()方法有可能可以释放锁对象。
当锁对象是一个Thread对象时, 调用该Thread对象的join()方法可以释放锁对象,因为join()的实现是调用wait()方法。
public class TestJAVA {
        public static void main(String[] args) throws Exception {
              R r1= new R();
              R r2= new R();
              Thread t1= new Thread(r1);
              Thread t2= new Thread(r2);
              r1.set(t2);
              r2.set(t2);
              t1.setName( "t1");
              t2.setName( "t2");    
              t1.start();
              t2.start();   
       }
}
class R implements Runnable {
       Thread t;
        public void set(Thread t){
               this.t =t;
       }
        public void run() {
               synchronized (t ) {
                      try {
                            if(Thread.currentThread().getName().equals( "t1"))
                            t.join();
                           System. out.println(Thread.currentThread().getName());
                     } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                           e.printStackTrace();
                     }
              }
       }
}
结果:
t2
t1

7.死锁
Java编程语言中没有任何东西可以避免或打破死锁现象。必须仔细设计程序,以确保不会出现死锁。

8.锁测试与超时(lock()方法 v.s. tryLock() v.s. tryLock(long,TimeUnit)v.s. lockInterruptibly() await v.s. awaitUninterruptibly())
1)java.util.concurrent.locks中的Lock接口的lock()方法来获得另一个线程所持有的锁的时候,很可能发生阻塞。
2)如果用tryLock()方法试图申请一个锁,在成功获得锁后返回true,否则,立即返回false,而且线程可以立即离开去做其他事情。
一般使用结构:
if(myLock.tryLock()){
     try{}finally{myLock.unlock()}
}
else{
     // do something
}
3)如果再加上时间参数,即调用tryLock(long,TimeUnit)则可以设定一个阻塞的超时时间
tryLock(long,TimeUnit)进入TIMED WAITING状态
public class TestJAVA {
    public static void main(String[] args) throws Exception {
          R r1 = new R();
          Thread t1 = new Thread(r1);
          Thread t2 = new Thread(r1);
          t1.setName( "t1" );
          t2.setName( "t2" );
          t1.start();
          t2.start();
          TimeUnit. SECONDS .sleep(1);
          System. out .println("t2" + t2.getState());

   }
}

class R implements Runnable {
   Lock lock = new ReentrantLock();

    public void run() {
           try {
                  lock.tryLock(3, TimeUnit. SECONDS );
                 int i=1;
                  while(i>0){}
                  lock.unlock();
          } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                 e.printStackTrace();
          }
   }
}
结果:
t2TIMED_WAITING

4)也可以调用lockInterruptibly(),这相当于一个超时设为无限的tryLock()方法
调用lockInterruptibly(),线程也会进入WAITING状态
5)对于await()方法,如果另一个线程调用signalAll()或signal()或者超时时限已达到或者线程被中断,那么await()方法将返回。
如果希望await()方法 被中断时可以继续等待(即不抛出InterruptedException),可以使用awaitUninterruptibly()代替await()。

几个方法的对比
lock()          会阻塞,不响应InterruptedException
tryLock()     不阻塞,不响应InterruptedException
tryLock(long,TimeUnit) 规定时间内阻塞,响应InterruptedException
lockInterruptibly() 会阻塞,响应InterruptedException

await()      会阻塞,响应InterruptedException(进入WAITING状态)
awaitUninterrupeibly()会阻塞,不响应InterruptedException  (进入WAITING状态)

9.读/写锁
java.util.concurrent.locks.ReentrantReadWriteLock类
允许对读者线程共享访问,写者线程必须是互斥访问。
使用读/写锁步骤:
1)构造一个ReentrantReadWriteLock对象:
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
2)抽取读锁和写锁:
     private Lock readLock=rwl.readLock();
     private Lock writeLock=rwl.writeLock();
3)对所有的访问者加读锁:
     public void read(){
          readLock.lock();
          try{     //  do something      }
          finally{ readLock.unlock();}
     }
4)对所有的修改者加写锁:
     public void write(){
          write.lock();
          try{}
          finally{writeLock.unlock();}
     }
具体怎么用不清楚啊

10.过时方法stop()和suspend()和resume()
stop()和suspend()方法都试图控制一个给定线程的行为。
stop()方法天生就不安全,该方法终止所有未结束的方法,包括run()方法。当线程被终止,立即释放被它锁住的所有对象的所有对象的锁。这会导致对象处于不一致的状态。当线程要终止另一个线程时,无法知道什么时候调用stop()方法是安全的,什么时候导致对象被破坏。因此,该方法被弃用了。
suspend()与stop()不同,它不会破坏对象。但是,如果用suspend()挂起一个持有一个锁的线程,那么,该锁在恢复之前是不可用的。如果调用suspend()方法的线程试图获得同一个锁,那么程序就会死锁:被挂起的线程等着被恢复,而将其挂起的线程等待获得锁。
resume()被弃用的原因书上没明确讲,估计resumen()本来是让被suspend()挂起的线程重新运行的,现在suspend()被弃用了,resume()也就一起被弃用了吧。

11.阻塞队列
对于许多线程问题,可以通过使用一个或多个队列以优雅且安全的方式将其形式化。使用队列,可以安全地从一个线程向另一个线程传递数据。
在协调多个线程之间的合作时,阻塞队列是一个有用的工具。

阻塞队列方法:
add       添加一个元素           如果队列满,则抛出IllegalStateException
element 返回队列的头元素     如果队列空,抛出NoSuchElementException
remove  移出并返回头元素     如果队列空,抛出NoSuchElementException

offer     添加一个元素并返回true  如果队列满,返回false
peek     返回队列的头元素           如果对列空,则返回nul
poll       移出并返回队列的头元素  如果队列空,则返回null


put      添加一个元素                  如果队列满,则阻塞
take     移出并返回头元素            如果队列空,则阻塞

因为这些操作可能会返回null,所以队列中不能插入null。若插入,运行时抛出NullPointerException
另外有方法offer(E,long,TimeUnit)方法和poll(long,TimeUnit)方法 分别在队列满和队列空时阻塞,直至相应操作成功完成或超时。
java.util.concurrent包提供了阻塞队列的几个变种。
1)LinkedBlockingQueue 默认容量无上限,但是也可以选择指定最大容量 利用链表实现
2)LinkedBlockingDeque 双端的队列 利用链表实现
3)ArrayBlockingQueue  需指定容量 利用循环数组实现
4)PriorityBlockingQueue 带优先级的队列 若元素没有实现Comparable接口,编译不报错,运行会抛出ClassCastExceptionn
5)DelayQueue 元素必须实现Delayed接口,Delayed接口有方法getDelay()返回对象的残留延迟,负值表示延迟已经结束。元素只有在延迟用完的情况下才能从DelayQueue移除。此外,因为Delayed接口继承自Comparable接口,所以元素还必须实现compareTo方法,DelayQUeue用该方法对元素进行排序。若元素没有实现Delayed接口,编译将报错。
简单地利用阻塞队列完成生产者消费者模型:
public class TestJAVA {
        public static void main(String[] args) {
              Buffered buffered = new Buffered();
              Thread producer = new Thread(new Producer(buffered));
              Thread customer = new Thread(new Customer(buffered));
              producer.start();
              customer.start();
       }
}

class Producer implements Runnable {
       Buffered buffered;

        public Producer(Buffered buffered) {
               super();
               this.buffered = buffered;
       }

        @Override
        public void run() {
               // TODO Auto-generated method stub
               for (char c = 'a' ; c <= 'z'; c++) {
                      buffered.put(c);
                      try {
                           TimeUnit. MILLISECONDS.sleep(100);
                     } catch (InterruptedException e) {
                           e.printStackTrace();
                     }
              }
       }
}

class Customer implements Runnable {
       Buffered buffered;

        public Customer(Buffered buffered) {
               super();
               this.buffered = buffered;
       }

        @Override
        public void run() {
               // TODO Auto-generated method stub
               for (int i = 0; i < 26; i++) {
                     System. out.print(buffered .get() + "  ");
                      try {
                           TimeUnit. MILLISECONDS.sleep(300);
                     } catch (InterruptedException e) {
                           e.printStackTrace();
                     }
              }
       }
}

class Buffered {
       LinkedBlockingQueue<Character> buffer = new LinkedBlockingQueue<Character>(
                     10);

        public void put(Character a) {
               while (!buffer .offer(a)) {
              }
       }

        public char get() {
              Character result;
               while ((result = buffer .poll()) == null) {
              }
               return result;
       }
}
结果:
a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z  
不需要自己来完成同步操作,阻塞队列的实现者已经帮我们做好了。

12.线程安全的集合
前面的阻塞队列就属于线程安全的集合,还有另一种线程安全的集合。
java.util.concurrent包提供了映像、有序集和队列的高效实现:
ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue
与大多数集合不同,size()方法不必在常量时间内操作。确定这样的集合当前的大小通常需要遍历。
集合返回弱一致性的迭代器,这意味着迭代器不一定能反映出他们被构造之后的所有的修改。
如果集合在迭代器构造之后发生改变,java.util包中的迭代器会抛出ConcurrentModificationException
但线程安全的集合的迭代器不会抛错。 
除此之外,线程安全集合还有CopyOnWriteArrayList和CopyOnWriteArraySet。这两个集合的所有可变操作都对底层数组进行复制。对于读线程多于写线程的情况,这样的安排很有意义。构造出来的迭代器会一直引用旧数组(不能使用remoove(),若使用将抛出UnsupportedOperationException),即如果数组后来被修改了,迭代器将仍然引用旧数组。
任何集合类可以通过使用Collections工具类中的同步包装器(synchronized wrapper)变成线程安全的。
Collecions。synchronizedXXX();
可是如果另一个线程可能要利用迭代器对集合进行操作(包括使用for-each语句),仍然要加锁。否则抛出ConcurrentModificationException。
e.g. 线程一使用同步包装器集合  List synchronizedList=Collections.synchronizedList(new ArrayList());
     线程二要使用迭代器   必须加锁
     synchronized(synchronizedList){  
       for(Object o: synchronizedList){ // 其他操作 }
      }
最好使用java.util.concurrent包中定义的集合,不使用同步包装器。

13.Callable与Future
Runnable封装的任务没有参数和返回值。

Callable和Runnable相似,但有返回值。可以替代Runnable接口
Callable<V>{ V call(){}}

Future保存异步计算的结果,可以启动一个计算,将Future对象交给某个线程。Future对象的所有者在计算好之后就可以获得它。
Future<V>{ 
V get() throws ... 调用后阻塞,直到计算完成。中断抛出InterruptedException 若任务取消成功,调用此方法会抛出       
                   CancellationException                                   
V get(long,TimeUnit)throw ... 计算完成之前,调用超时返回TimeoutException。中断抛出InterruptedException
                              若任务取消成功,调用此方法会抛出CancellationException
void cancel(boolean) 如果计算还没开始,它被取消且不再开始(此时不管boolean参数)。如果计算处于运行之中,boolean参数为true           
                     的话则中断任务。            
boolean isCancelled()如果任务在完成前被取消,返回true
boolean isDone() 如果计算还没完成返回false,否则返回true。任务取消后,此方法也返回true
}

FutureTask包装器是一种非常便利的机制,可将Callable转换成Future和Runnable,它同时实现二者的接口。
构造方法可以传递Callable和Runnable接口
FutureTask(Callable<V>)
FutureTask(Runnable,T)其中run方法成功完成后返回指定结果T,若run方法抛出异常则不返回结果T
要执行任务直接将FutureTask对象传递给Thread的构造函数即可。
e.g. 
class R implements Runnable{ public void run(){}  }
new Thread(new FutureTask(new R(),result));

14.执行器
java.util.concurrent包中
如果程序中创建了大量的线程,应该使用线程池。
线程池有两点好处:
      1)当run方法退出时,线程不会死亡,而是在池中准备为下一个请求提供服务。
      2)可以减少并发线程的数目。          
执行器(Executors)类有许多静态工厂方法用来创建线程池。(注意有一个接口名为Executor)
newCachedThreadPool           必要时创建线程,空闲线程会被保留60秒 如果有空线程可用,立即让它执行任务,否则创建一个新线程
newFixedThreadPool            该池包含固定数量的线程,空闲线程会一直被保留 
newSingleThreadExecutor       只有一个线程的“池”,该线程顺序执行每一个提交的任务
newScheduledThreadPool        用于预定执行而构建的固定线程池,替代java.util.Timer
newSingleThreadScheduledExecutor 用于预定执行而构建的单线程“池”

前三个方法:  返回一个实现了ExecutorService接口的ThreadPoolExecutor对象。
后两个方法:  返回一个实现了ScheduledExecutorService接口的对象。(ScheduledExecutorService接口继承自ExecutorService接
             口)

将任务提交给线程池的方法(可以利用返回的Future对象获取该任务的状态):
      Future<?> submit(Runnable task)       Future的get方法在完成时只返回null
      Future<T> submit(Runnable task,T result)  Future的get方法在完成时返回指定的result
      Future<T> submit(Callable<T> task)    Future的get方法在完成时返回计算的结果

当用完一个线程池后,必须要关闭线程池。有两种关闭方法:
      1)调用shutdown()。被关闭的线程池将不再接受新的任务,当所有任务完成以后,线程池中的线程死亡
      2)调用shutdownNow()。该方法将取消线程池中所有未开始的任务,并试图中断正在运行的线程。

15.同步器
java.util.concurrent包中

CyclicBarrier
CountDownLatch
Exchanger
Semaphore
SynchronousQueue
这些以后要是用到在慢慢地写吧。。。

原创粉丝点击