线程复习笔记

来源:互联网 发布:c语言合法标识符 编辑:程序博客网 时间:2024/05/21 17:58

1 线程的三种方式:
    A 实现Runnable接口  new Thread(new MyRunnable()).start();
    B 继承Thread类
    C Callable与Future方式
2 中断线程
    interrupt方法请求终止线程(线程的中断状态被置位)
    isInterrupted方法,检查这个中断状态
    while( !Thread.currentThread().isInterrupted() ) {  do more work!}
    注:有个静态方法interrupted,也是用来测试当前线程是否被中断,但是这一方法会产      生副作用--他将当前现在的中断状态置为false.
3 线程的六种状态:
    new(新生)、runnable(可运行)、blocked(阻塞)、
    waitng(等待)、timed waiting(计时等待)、terminated(终止)
    当一个线程视图获取一个内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态。当线程等待另一个线程通知调度时,他自己进入等待状态。如调用Object.wait或者
     Thread.join
4 wait和sleep区别
    a:sleep() 方法是使线程停止运行一段时间,sleep时间间隔期满后,线程不一定立即恢       复运行。
      wait()是线程交互式,如果线程对一个同步对象(方法)发出一个wait调用,改线程会暂停执行,被调用对象(方法)进入等待状态,直到被唤醒。
    b:Sleep时别的线程也不可以访问锁定对象(睡着也抱着锁); 
       Wait时别的线程可以访问锁定对象(调用wait,锁就撒手);
5 锁
    a:synchronized 如:public synchronized void push(char c);
    b:java.util.concurrent.locks.ReentrantLock类,
    ReentrantLock(boolean fair)
    这个构造方法还可以创建一个带有公平策略的锁。公平锁偏爱等待时间最长的线程。但是,这一公平的保证将大大降低性能。
    注:如果线程调度器选择忽略一个线程,而该线程为了这个锁已经等待很长时间了,哪么也没有几乎公平处理这个锁。
    class X {
      private final ReentrantLock lock = new ReentrantLock();
      public void m() { 
        lock.lock(); // block until condition holds
      try {
         // ... method body
      } finally {
      lock.unlock()
      }
     }
    }
 补充volatile关键字
    有时候为了读写一两个实例域就使用同步,显然开销会很大。这里推荐使用volatile关键字。
         private boolean done;
         public synchronized boolean getDone(){return done;}
         public synchronized void setDone(boolean done) {this.done = done;}
      相当于:
         private volatile boolean done;
         public boolean getDone(){return done;}
         public void setDone(boolean done) {this.done = done;}


6 新的锁控制和应用例子
class Bank
{
   private Lock bankLock;
   private Condition sFouds;


   public Bank()
   {
      bankLock = new ReenTrantLock();
      sFouds = bankLock.newCondition();//条件叫余额充足
   }


   public void transfer()
  {
     backLock.lock();
     while(acounts[forms] < amount)
    {
        sFouds.await();
    }
    //do more work
    sFouds.signalAll();
   }
}


7.内部的锁和条件存在一些局限(synchronized)
    a不能中断一个正在试图获得锁的线程。
    b视图获得锁时不能设定超时。
    c每个锁仅有单一条件,可能不够。


8.特殊锁
    synchronized(obj) { ... }
    使用一个对象的锁定来实现额外的原子操作,称为客户端锁定。client-side locking
    客户端锁定非常脆弱,通常不推荐使用。


9.读写锁
  java.util.concurrent.locks.ReentrantReadWriteLock类
  如果很多线程从一个数据结构读取数据,而很少线程修改其中的数据的话,此类非常有用。
  private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
  private Lock readLock = rwl.readLock();
  private Lock writeLock = rwl.writeLock();
  public doble getTBalance()
  {
    readLock.lock();
    try { ... }
    finally {readLock.unlock();}
  }
  public void transfer()
 {
    writeLock.lock();
    try { ... }
    finally {writeLock.unlock();}
  }


10.死锁
  public class DeadLock extends Thread { 


  int flag=0; 
  static Object o1=new Object(); 
  static Object o2=new Object(); 


  public DeadLock(int flag){ 
     this.flag=flag; 
   } 
  @Override 
  public void run() { 
     if(this.flag==1){ 
       synchronized(o1){ 
          System.out.println(Thread.currentThread().getName()+"锁定对象o1"); 
          try { 
              Thread.sleep(1000);//让另一个线程能进入另一块区域,锁定对象o2 
          } catch (InterruptedException e) { 
              e.printStackTrace(); 
          } 
         synchronized(o2){ 
              System.out.print("this.flag="+this.flag); 
          } 
       } 
     }
     if(this.flag==2){ 
         synchronized(o2){ 
            System.out.println(Thread.currentThread().getName()+"锁定对象o2"); 
            try { 
                 Thread.sleep(1000);//让另一个线程能进入另一块区域,锁定对象o1 
             } catch (InterruptedException e) { 
                         e.printStackTrace(); 
             } 
             synchronized(o1){ 
                 System.out.print("this.flag="+this.flag); 
             } 


         } 
     }
  } 


 public static void main(String []args){ 
    DeadLock d1=new DeadLock(1); 
    DeadLock d2=new DeadLock(2); 
    d1.start(); 
    d2.start(); 
  } 
}




11.经典生产者与消费者
class SyncStack{ 
   private int index = 0; 
   private char []data = new char[6]; 


   public synchronized void push(char c){ 
      if(index == data.length){ 
       try{ 
         this.wait(); 
       }catch(InterruptedException e){} 
      } 
      this.notify(); 
      data[index] = c; 
      index++; 
   } 
   public synchronized char pop(){ 
      if(index ==0){ 
        try{ 
          this.wait(); 
        }catch(InterruptedException e){} 
      } 
      this.notify(); 
      index--; 
      return data[index]; 
   } 
  }


 class Producer implements Runnable{ 
      SyncStack stack; 
      public Producer(SyncStack s){ 
         stack = s; 
       } 
      public void run(){ 
        for(int i=0; i<20; i++){ 
         char c =(char)(Math.random()*26+'A');// 
         stack.push(c); 
         System.out.println("produced:"+c); 
         Thread.sleep((int)(Math.random()*1000)); 
        } 
     } 
 } 


class Consumer implements Runnable{ 
   SyncStack stack; 
   public Consumer(SyncStack s){ 
      stack = s; 
    } 
   public void run(){ 
      for(int i=0;i<20;i++){ 
         char c = stack.pop(); 
         System.out.println("消费:"+c); 
         Thread.sleep((int)(Math.random()*1000)); 
      } 
   } 
}


public class ProduceConsumer { 
   public static void main(String args[]){ 
      SyncStack stack = new SyncStack(); 
      Runnable p=new Producer(stack); 
      Runnable c = new Consumer(stack); 
      Thread t1 = new Thread(p); 
      Thread t2 = new Thread(c); 
      t1.start(); 
      t2.start(); 
   } 
}




12 阻塞队列
  阻塞队列方法分3类,取决与队列满时或空时他们的响应方式,
    如果将队列当做线程管理工具来使用,将要使用put和take方法,满或空,线程阻塞。
    add、 element 、remove和操作抛出异常。
    在一个多线程程序中,队列会在任何时候空或者满,offer、poll、peek,这些方法有返回值。
    java.util.concurrent包提供了阻塞队列的几个变种
       LinkedBlockingQueue 容量默认为int的最大值,也可以指定最大容量。
       LinkedBlockingDeque 是一个双端版本。
       ArrayBlockingQueue 在构造时需指定容量,并且有个可选的参数来指定是否需要公平性。若设置了公平参数,则等待最长时间的线程会优先处理,
                          这一设置会降低性能。
       PriorityBlockingQueue 带优先级的队列




13 Callable与Future
    Runable封装的一个异步运行的任务,可以把它想象成一个没有参数和返回值的异步方法。
    Callable与Runnable类似,但是有返回值。Future保存异步计算的记过,get方法调用被阻塞,直到计算完成。
    FutureTask包装器是一种非常便利的机制,可将Callable接口转换成Future和Runable,它同时实现二者接口。
   例如:
   Callable<Integer> myComputation = ...;
   FutureTask<Integer> task = new FutureTask<Integer>(myComputation)
   new Thread(task).start();
    Integer result = task.get();
例如:
   class MatchCounter implements Callable<Integer>
   {
       private File startDir;
       private String keyword;
       private int count;


       public MatchCounter(File startDir,String keyword)
      {
         this.startDir = startDir;
         this.keyword = keyword;
      }


      @Override
      public Integer call() throws Exception
     {
        count = 0;


        File[] files = startDir.listFiles();
        ArrayList<Future<Integer>> results = new ArrayList<Future<Integer>>();


        for(File f: files) {
          if(f.isDirectory()){
                MatchCounter counter = new MatchCounter(f,keyword);
                FutureTask<Integer> task = new FutureTask<Integer>(counter);
                results.add(task);
                new Thread(task).start(); 
          } else{
            if(search(f)){
                count ++;
            }
          }
       }


    for(Future<Integer> result : results)
    {
      count += result.get();
    }
      return count;
   }


public boolean search(File file) {
    boolean flag = false;
    try
    {
        Scanner in = new Scanner(file);
       while(in.hasNextLine() && !flag)
       {
           String str = in.nextLine();
           if(str.contains(keyword))
           {
               flag = true;
            }
        }
        in.close();
     }
      catch (FileNotFoundException e)
     {
        e.printStackTrace();
     } 
      return flag;
    }
}




14 执行器Executor
   使用线程池理由:
      a:线程池包含许多空闲线程,当一线程退出时,线程不会死亡,而是在池中准备为下一请求提供服务。
      b:使用线程池可以减少并发线程的数目。(创建固定线程数的线程池)
    Executors类有许多静态工厂方法用来构建线程池,如:
       newCachedThreadPool 必要时创建新线程;空闲线程会被保留60秒
       newFuxedThreadPool 该池包含固定数量的线程;空闲线程一致被保留
       newSingleThreadExecutor 只有一个线程的池,该线程顺序执行每一个提交的任务
       newScheduledThreadPool 用于预定执行而构建的固定线程池,替代java.util.Timer
       newSingleThreadScheduledExecutor 用于预定执行而构建的单线程池。
   使用线程池应该做的事:
      1)调用Executors类中的静态方法产生线程池。
      2)调用submit提交Runable或Callable对象,该方法返回值为Future对象。
      3)如果想要取消一个任务,或如果提交Callable对象,那就要保存好返回的Future对象。
      4)当不再提交任务时,调用shutdown。
      ExecutorService pool = Executors.newCachedThreadPool();
      MatchCounter counter = new MathcCounter(...);
      Futurn<Integer> countResult = pool.submit(counter);


   ExecutorService接口常用方法
      invokeAll 执行给定的任务,当所有任务完成时,返回保持任务结果的 Future 列表
          List<Callable<T>> tasks = ...?;
          List<Future<T>> results = executorService.invokeAll(tasks);
      invokeAny 执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。
          <T> T invokeAny(Collection<? extends Callable<T>> tasks)
      shutdown 启动线程依此顺序关闭,执行以前提交的任务,但不接受新任务
      List<Runnable> shutdownNow() 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
      submit 提交一个任务。




15 预定执行
   ScheduleExecutorService接口具有为预定执行(Scheduled Execution)或重复执行而设计的方法。
   Executors类的newScheduledThreadPool 和newSingleThreadScheduledExecutor方法返回实现了ScheduledExecutorService接口的对象。
   java.util.concurrent.ScheduledExecutorService:
        ScheduledFuture<?> schedule(Runnable command, long delay,TimeUnit unit) 预定在指定的时间之后执行
       ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
          command - 要执行的任务
          initialDelay - 首次执行的延迟时间
          period - 连续执行之间的周期
          unit - initialDelay 和 period 参数的时间单位




16 门闩java.util.concurrent.CountDownLatch
   CountDownLatch
     一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 
     await() 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 
     await(long timeout, TimeUnit unit) 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。 
     countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
     CountDownLatch 很适合用来将一个任务分为n个独立的部分,等这些部分都完成后继续接下来的任务,CountDownLatch 只能出发一次,计数值不能被重置。
     例:一个项目可以分为多个模块,只有但这些模块都完成后才可以继续下一步的工作。
     class Module implements Callable<Integer>
     {
        private CountDownLatch latch;
        private String moduleName;
        private int time;


        public Module(CountDownLatch latch, String moduleName, int time)
       {
           this.latch = latch;
           this.moduleName = moduleName;
           this.time = time;
        }


        private void work() throws InterruptedException
        {
           TimeUnit.MILLISECONDS.sleep(time);
           System.out.println(moduleName + " 完成,耗时:" + time);
         }


       @Override
       public Integer call() throws Exception
       {
          work();
          latch.countDown();
          return time;
       }
     }


    class Controller implements Runnable
    {
          private CountDownLatch latch; 
          private List<Future<?>> runResults; 
          int count = 0;


          public Controller(CountDownLatch latch,List<Future<?>> runResults)
         {
            this.latch = latch;
            this.runResults = runResults;
          }


         @Override
         public void run()
         {
            latch.await();
            for(Future<?> f : runResults)
            {
                count = count + (Integer)f.get();
            } 
            System.out.println("所有模块都完成,耗时:" + count); 
          }
     }


     public class Project
     {
         static final int SIZE = 20;
         public static void main(String[] args) throws InterruptedException, ExecutionException
        {
             List<Future<?>> runResults = new ArrayList<Future<?>>(); 
             CountDownLatch latch = new CountDownLatch(SIZE); 
             ExecutorService exec = Executors.newCachedThreadPool(); 
             for (int i = 0; i < SIZE; i++)
             {
                 Future temp = exec.submit(new Module(latch, "模块" + (i + 1), (i + 1)));
                 runResults.add(temp);
              }


              Controller controller = new Controller(latch,runResults);
              exec.submit(controller);


              exec.shutdown();


        }
    }




17 栅栏java.util.concurrent.CyclicBarrier


       一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point),线程才继续开始执行任务.
       在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该barrier在释放等待线程后可以重用,
       所以称它为循环的barrier。CyclicBarrier可以多次重复使用.


       await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待
       await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。


        3个玩家同时进行通关游戏,当他们都到达或通过某一关后,才可继续往下过关.


        class GameGate
        {
           // 关名称
           private String barrierName;
           // 玩家通关时间
           private int passTime;


           public GameGate(String barrierName, int passTime)
           {
                this.barrierName = barrierName;
                this.passTime = passTime;
           }


          public String getBarrierName()
          {
              return barrierName;
           }


          public int getPassTime()
          {
              return passTime;
           }
       }




      class GameRunable implements Runnable
      {


         // 游戏关集合
         private List<GameGate> gameGates;


         // 玩家名称
         private String gamePlayer;


         private CyclicBarrier cyclicBarrier;


         public GameRunable(List<GameGate> gameGates, String gamePlayer,CyclicBarrier cyclicBarrier)
        {
               this.cyclicBarrier = cyclicBarrier;
               this.gameGates = gameGates;
               this.gamePlayer = gamePlayer;
         }


         public void run()
         {
            try
           {
              for (GameGate gameBarrier : gameGates)
              {
                    Thread.sleep(gameBarrier.getPassTime() * 1000);
                    System.out.println(gamePlayer + " passed game barrier " + gameBarrier.getBarrierName());
                    cyclicBarrier.await();
               }
           } 
         }


       public static void main(String[] args) {
          CyclicBarrier barrier = new CyclicBarrier(3);


          // 为玩家1建立通关任务
          List<GameGate> playerOneGates = new ArrayList<GameGate>(4);
          playerOneGates.add(new GameGate("第一关", 5));
          playerOneGates.add(new GameGate("第二关", 60));
          playerOneGates.add(new GameGate("第三关", 8)); 
          GameRunable task1 = new GameRunable(playerOneGates, "张三", barrier);


         // 为玩家2建立通关任务
          List<GameGate> playerTwoGates = new ArrayList<GameGate>(4);
          playerTwoGates.add(new GameGate("第一关", 1));
          playerTwoGates.add(new GameGate("第二关", 6));
          playerTwoGates.add(new GameGate("第三关", 3));
          GameRunable task2 = new GameRunable(playerTwoGates, "李四", barrier);


         // 为玩家3建立通关任务
         List<GameGate> playerThreeGates = new ArrayList<GameGate>(4);
         playerThreeGates.add(new GameGate("第一关", 7));
         playerThreeGates.add(new GameGate("第二关", 6));
         playerThreeGates.add(new GameGate("第三关", 4));
         GameRunable task3 = new GameRunable(playerThreeGates, "王五", barrier);


         ExecutorService executorService = Executors.newFixedThreadPool(3);
         executorService.submit(task1);
         executorService.submit(task2);
         executorService.submit(task3);
         executorService.shutdown();
      }

原创粉丝点击