热门关键字:
jquery > jquery教程 > html5 > LockSupport深入浅出

LockSupport深入浅出

208
作者:管理员
发布时间:2020/3/25 10:50:16
评论数:0
转载请自觉注明原文:http://www.jq-school.com/Show.aspx?id=1102

  本篇是《自己动手写把"锁"》系列技术铺垫的最后一个知识点。本篇主要讲解LockSupport工具类,它用来实现线程的挂起和唤醒。

  LockSupport是Java6引入的一个工具类,它简单灵活,应用广泛。

  一、简单

  俗话说,没有比较就没有伤害。这里咱们还是通过对比来介绍LockSupport的简单。

  在没有LockSupport之前,线程的挂起和唤醒咱们都是通过Object的wait和notify/notifyAll方法实现。

  写一段例子代码,线程A执行一段业务逻辑后调用wait阻塞住自己。主线程调用notify方法唤醒线程A,线程A然后打印自己执行的结果。

  执行这段代码,不难发现这个错误:

  Exceptioninthread"main"java.lang.IllegalMonitorStateException

  atjava.lang.Object.notify(NativeMethod)

  原因很简单,wait和notify/notifyAll方法只能在同步代码块里用(这个有的面试官也会考察)。所以将代码修改为如下就可正常运行了:

  复制代码

  publicclassTestObjWait{

  publicstaticvoidmain(String[]args)throwsException{

  finalObjectobj=newObject();

  ThreadA=newThread(newRunnable(){

  @Override

  publicvoidrun(){

  intsum=0;

  for(inti=0;i<10;i++){

  sum+=i;

  }

  try{

  synchronized(obj){

  obj.wait();

  }

  }catch(Exceptione){

  e.printStackTrace();

  }

  System.out.println(sum);

  }

  });

  A.start();

  //睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法

  Thread.sleep(1000);

  synchronized(obj){

  obj.notify();

  }

  }

  }

  复制代码

  那如果咱们换成LockSupport呢?简单得很,看代码:

  直接调用就可以了,没有说非得在同步代码块里才能用。简单吧。

  二、灵活

  如果只是LockSupport在使用起来比Object的wait/notify简单,那还真没必要专门讲解下LockSupport。最主要的是灵活性。

  上边的例子代码中,主线程调用了Thread.sleep(1000)方法来等待线程A计算完成进入wait状态。如果去掉Thread.sleep()调用,代码如下:

  复制代码

  publicclassTestObjWait{

  publicstaticvoidmain(String[]args)throwsException{

  finalObjectobj=newObject();

  ThreadA=newThread(newRunnable(){

  @Override

  publicvoidrun(){

  intsum=0;

  for(inti=0;i<10;i++){

  sum+=i;

  }

  try{

  synchronized(obj){

  obj.wait();

  }

  }catch(Exceptione){

  e.printStackTrace();

  }

  System.out.println(sum);

  }

  });

  A.start();

  //睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法

  //Thread.sleep(1000);

  synchronized(obj){

  obj.notify();

  }

  }

  }

  复制代码

  多运行几次上边的代码,有的时候能够正常打印结果并退出程序,但有的时候线程无法打印结果阻塞住了。原因就在于:主线程调用完notify后,线程A才进入wait方法,导致线程A一直阻塞住。由于线程A不是后台线程,所以整个程序无法退出。

  那如果换做LockSupport呢?LockSupport就支持主线程先调用unpark后,线程A再调用park而不被阻塞吗?是的,没错。代码如下:

  复制代码

  publicclassTestObjWait{

  publicstaticvoidmain(String[]args)throwsException{

  finalObjectobj=newObject();

  ThreadA=newThread(newRunnable(){

  @Override

  publicvoidrun(){

  intsum=0;

  for(inti=0;i<10;i++){

  sum+=i;

  }

  LockSupport.park();

  System.out.println(sum);

  }

  });

  A.start();

  //睡眠一秒钟,保证线程A已经计算完成,阻塞在wait方法

  //Thread.sleep(1000);

  LockSupport.unpark(A);

  }

  }

  复制代码

  不管你执行多少次,这段代码都能正常打印结果并退出。这就是LockSupport最大的灵活所在。

  总结一下,LockSupport比Object的wait/notify有两大优势:

  ①LockSupport不需要在同步代码块里。所以线程间也不需要维护一个共享的同步对象了,实现了线程间的解耦。

  ②unpark函数可以先于park调用,所以不需要担心线程间的执行的先后顺序。

  三、应用广泛

  LockSupport在Java的工具类用应用很广泛,咱们这里找几个例子感受感受。以Java里最常用的类ThreadPoolExecutor为例。先看如下代码:

  复制代码

  publicclassTestObjWait{

  publicstaticvoidmain(String[]args)throwsException{

  ArrayBlockingQueue<Runnable>queue=newArrayBlockingQueue<Runnable>(1000);

  ThreadPoolExecutorpoolExecutor=newThreadPoolExecutor(5,5,1000,TimeUnit.SECONDS,queue);

  Future<String>future=poolExecutor.submit(newCallable<String>(){

  @Override

  publicStringcall()throwsException{

  TimeUnit.SECONDS.sleep(5);

  return"hello";

  }

  });

  Stringresult=future.get();

  System.out.println(result);

  }

  }

  复制代码

  代码中我们向线程池中扔了一个任务,然后调用Future的get方法,同步阻塞等待线程池的执行结果。

  这里就要问了:get方法是如何组塞住当前线程?线程池执行完任务后又是如何唤醒线程的呢?





如果您觉得本文的内容对您的学习有所帮助:支付鼓励



关键字:html
友荐云推荐