Java Concurrency Programming

Java Concurrency Programming PowerPoint PPT Presentation


  • 86 Views
  • Uploaded on
  • Presentation posted in: General

Agenda. Multi-Thread IntroductionJava Memory ModeSynchronizedConcurrent Package IntroductionDeadlockThread PoolMulti-thread Anti-patternConcurrent Programming Best PracticeQ

Download Presentation

Java Concurrency Programming

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


1. Java Concurrency Programming [email protected]

2. Agenda Multi-Thread Introduction Java Memory Mode Synchronized Concurrent Package Introduction Deadlock Thread Pool Multi-thread Anti-pattern Concurrent Programming Best Practice Q&A

3. Multi-Thread introduction -related conception What is thread? Concurrent VS Parallel Performance VS Multi-Thread ????:??????cpu????????????????. Concurrent(??), parallel(??):????????,????????,???????????????,?????????,???????cpu????????????????????????,?????????java???????????? Performance?multi-thread:??????,?????????????,????????cpu?,????memory?,????I/O?,?????????????,??????I/O?????CPU,memory????,?CPU???memory?I/O??,??????CPU??,??????????????????????,??????????????,???????????? ????:??????cpu????????????????. Concurrent(??), parallel(??):????????,????????,???????????????,?????????,???????cpu????????????????????????,?????????java???????????? Performance?multi-thread:??????,?????????????,????????cpu?,????memory?,????I/O?,?????????????,??????I/O?????CPU,memory????,?CPU???memory?I/O??,??????CPU??,??????????????????????,??????????????,????????????

4. Multi-Thread introduction -Thread Life Circle

5. Multi-Thread introduction -Thread Safe* hashMap,ConcurrentHashMap???????????hashMap,ConcurrentHashMap???????????

6. ????????? ?????? ThreadLocal ?? ?????????????? ??????????? ???????? Multi-Thread introduction -Thread Safe*

7. Java Memory Mode ??????? Visibility ??? (??,?????????) Ordering ??? (??????????????) JMM???????? ?jmm?, ???????????, ?????????????, ????????? JMM???????? ??Java????????volatile???, ?????????? JMM??????: ??????, ?????????, ?????????????????????. ???????????????????????. ?volitile????????????, ????????????. ?????? Thread.start() ?? ????????????????. ???????? ????????? Thread.join()?????????????. ??????? Visibility ??? (??,?????????) Ordering ??? (??????????????) JMM???????? ?jmm?, ???????????, ?????????????, ????????? JMM???????? ??Java????????volatile???, ?????????? JMM??????: ??????, ?????????, ?????????????????????. ???????????????????????. ?volitile????????????, ????????????. ?????? Thread.start() ?? ????????????????. ???????? ????????? Thread.join()?????????????.

8. Synchronized -example1

9. Synchronized -example2

10. Synchronized -notify, notifyAll, wait* package com.webex.joe; import java.util.concurrent.CountDownLatch; public class SyncExample { public static void main(String[] args) { final CountDownLatch begin = new CountDownLatch(1); final CountDownLatch end = new CountDownLatch(2); Thread threadA,threadB; final ClassA shareObjA = new ClassA(); final ClassA shareObjB = new ClassA(); threadA = new Thread(new Runnable(){ public void run() { try { begin.await(); shareObjA.method7("abc"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }finally{ end.countDown(); } } }); threadB = new Thread(new Runnable(){ public void run() { try { begin.await(); shareObjA.method7("abc"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }finally{ end.countDown(); } } }); threadA.setName("ThreadA"); threadA.start(); threadB.start(); threadB.setName("ThreadB"); begin.countDown(); try { end.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } class ClassA{ public static void method1() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("no synchronized static method " + Thread.currentThread().getName()); } } public synchronized static void method2() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized static method " + Thread.currentThread().getName()); } } public void method3() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("no synchronized instance method " + Thread.currentThread().getName()); } } public synchronized void method4() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized instance method " + Thread.currentThread().getName()); } } public void method5() throws InterruptedException{ synchronized(this){ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized block instance obj " + Thread.currentThread().getName()); } } } public void method6() throws InterruptedException{ synchronized(this.getClass()){ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized block class obj " + Thread.currentThread().getName()); } } } public void method7(String obj) throws InterruptedException{ synchronized(obj){ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized String obj " + Thread.currentThread().getName()); } } } } package com.webex.joe; import java.util.concurrent.CountDownLatch; public class SyncExample { public static void main(String[] args) { final CountDownLatch begin = new CountDownLatch(1); final CountDownLatch end = new CountDownLatch(2); Thread threadA,threadB; final ClassA shareObjA = new ClassA(); final ClassA shareObjB = new ClassA(); threadA = new Thread(new Runnable(){ public void run() { try { begin.await(); shareObjA.method7("abc"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }finally{ end.countDown(); } } }); threadB = new Thread(new Runnable(){ public void run() { try { begin.await(); shareObjA.method7("abc"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }finally{ end.countDown(); } } }); threadA.setName("ThreadA"); threadA.start(); threadB.start(); threadB.setName("ThreadB"); begin.countDown(); try { end.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } class ClassA{ public static void method1() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("no synchronized static method " + Thread.currentThread().getName()); } } public synchronized static void method2() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized static method " + Thread.currentThread().getName()); } } public void method3() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("no synchronized instance method " + Thread.currentThread().getName()); } } public synchronized void method4() throws InterruptedException{ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized instance method " + Thread.currentThread().getName()); } } public void method5() throws InterruptedException{ synchronized(this){ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized block instance obj " + Thread.currentThread().getName()); } } } public void method6() throws InterruptedException{ synchronized(this.getClass()){ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized block class obj " + Thread.currentThread().getName()); } } } public void method7(String obj) throws InterruptedException{ synchronized(obj){ for (int i = 0; i < 10; i++) { Thread.sleep(100); System.out.println("synchronized String obj " + Thread.currentThread().getName()); } } } }

11. Concurrent Package Introduction -Atomic Type* package com.webex.joe; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; public class AtomicExample { public static void main(String[] args) { final Sequence sequence = new NoSyncSequence(); final CountDownLatch end = new CountDownLatch(100); for (int i = 0; i < 100; i++) { new Thread(new Runnable(){ @Override public void run() { try { for (int j = 0; j < 10; j++) { sequence.getSequenceNo(); Thread.sleep(100); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } end.countDown(); } }).start(); } try { end.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(sequence.getSequenceNo()); } } interface Sequence { int getSequenceNo(); } class AtomicSequence implements Sequence { private AtomicInteger count = new AtomicInteger(0); public int getSequenceNo() { return count.getAndAdd(1); } } class SyncSequence implements Sequence { private int count = 0; public synchronized int getSequenceNo() { return count++; } } class NoSyncSequence implements Sequence { private int count = 0; public int getSequenceNo() { return count++; } }package com.webex.joe; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; public class AtomicExample { public static void main(String[] args) { final Sequence sequence = new NoSyncSequence(); final CountDownLatch end = new CountDownLatch(100); for (int i = 0; i < 100; i++) { new Thread(new Runnable(){ @Override public void run() { try { for (int j = 0; j < 10; j++) { sequence.getSequenceNo(); Thread.sleep(100); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } end.countDown(); } }).start(); } try { end.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(sequence.getSequenceNo()); } } interface Sequence { int getSequenceNo(); } class AtomicSequence implements Sequence { private AtomicInteger count = new AtomicInteger(0); public int getSequenceNo() { return count.getAndAdd(1); } } class SyncSequence implements Sequence { private int count = 0; public synchronized int getSequenceNo() { return count++; } } class NoSyncSequence implements Sequence { private int count = 0; public int getSequenceNo() { return count++; } }

12. Concurrent Package Introduction -Synchronizer* BlockQueue ConcurrentLinkedQueue Semaphore CyclicBarrier CountDownLatch FutureTask Lock

13. Concurrent Package Introduction -Executor Framework* Executor ExecutorService CompletionService Executor:????? ExecutorService:???????????????? CompletionService:??????,??????????????Executor:????? ExecutorService:???????????????? CompletionService:??????,??????????????

14. Concurrent Package Introduction -Lock

15. Concurrent Package Introduction -Lock in DMS(1)* public void copy(ItemState state, boolean syncLastModifiedTime,boolean isSharedChildren) { ((NodeState) state).lock(true);// read lock lock.writeLock().lock(); try { NodeState nodeState = (NodeState) state; id = nodeState.id; parentId = nodeState.parentId; nodeName = nodeState.nodeName; nodeTypeName = nodeState.nodeTypeName; mixinTypeNames = nodeState.mixinTypeNames; defId = nodeState.defId; propertyNames = nodeState.propertyNames; sharedPropertyNames = true; nodeState.sharedPropertyNames = true; if(isSharedChildren){ childNodeManager.shareChildNodeEntries(nodeState.childNodeManager); } if (syncLastModifiedTime) { setLastModifiedTime(state.getLastModifiedTime()); modCount = state.getModCount(); } if (pMgr == null) { pMgr = nodeState.pMgr; } } finally { lock.writeLock().unlock(); ((NodeState) state).unlock(true); } } public void copy(ItemState state, boolean syncLastModifiedTime,boolean isSharedChildren) { ((NodeState) state).lock(true);// read lock lock.writeLock().lock(); try { NodeState nodeState = (NodeState) state; id = nodeState.id; parentId = nodeState.parentId; nodeName = nodeState.nodeName; nodeTypeName = nodeState.nodeTypeName; mixinTypeNames = nodeState.mixinTypeNames; defId = nodeState.defId; propertyNames = nodeState.propertyNames; sharedPropertyNames = true; nodeState.sharedPropertyNames = true; if(isSharedChildren){ childNodeManager.shareChildNodeEntries(nodeState.childNodeManager); } if (syncLastModifiedTime) { setLastModifiedTime(state.getLastModifiedTime()); modCount = state.getModCount(); } if (pMgr == null) { pMgr = nodeState.pMgr; } } finally { lock.writeLock().unlock(); ((NodeState) state).unlock(true); } }

16. Concurrent Package Introduction -Lock in DMS(2)* public class MultStateLock<T> { private ConcurrentMap<Thread,ConditionInfo<T>> condMap = new ConcurrentHashMap<Thread,ConditionInfo<T>>(); private ReentrantLock lock = new ReentrantLock(); final private Condition EMPTY_OBJ = lock.newCondition(); public void lock(Set<T> lockIds) throws InterruptedException{ lock.lock(); boolean isWait = false; try{ if(hasConflict(lockIds)){ isWait = true; Condition c = lock.newCondition(); condMap.put(Thread.currentThread(), new ConditionInfo<T>(lockIds,Thread.currentThread(),c)); c.await(); } if(!isWait){ condMap.put(Thread.currentThread(),new ConditionInfo<T>(lockIds,Thread.currentThread(),EMPTY_OBJ)); } }finally{ lock.unlock(); } } public void unlock(){ lock.lock(); try{ //????map??? ConditionInfo<T> conInfo = condMap.remove(Thread.currentThread()); Set<T> lockIds = conInfo.getLockSet(); //??map????????? Collection<ConditionInfo<T>> infos = condMap.values(); //???????????????,???????????? List<Set<T>> nonConflictSets = new ArrayList<Set<T>>(); List<Condition> nonConflictConds = new ArrayList<Condition>(); for (Iterator<ConditionInfo<T>> it = infos.iterator(); it.hasNext();) { ConditionInfo<T> conditionInfo = (ConditionInfo<T>) it.next(); if(hasIntersection(conditionInfo.getLockSet(),lockIds)&& conditionInfo.getCondition() != EMPTY_OBJ){ if(!hasConflict(nonConflictSets,conditionInfo.getLockSet())){ nonConflictSets.add(conditionInfo.getLockSet()); nonConflictConds.add(conditionInfo.getCondition()); } } } //?????????,???????????? for (Iterator<Condition> it = nonConflictConds.iterator(); it.hasNext();) { Condition cond = it.next(); cond.signal(); } }finally{ lock.unlock(); } } private boolean hasConflict(Collection<Set<T>> collection, Set<T> set){ for (Iterator<Set<T>> it = collection.iterator(); it.hasNext();) { Set<T> setA = it.next(); if(hasIntersection(setA,set)){ return true; } } return false; } private boolean hasConflict(Set<T> lockIds){ Collection<ConditionInfo<T>> infos = condMap.values(); for (Iterator<ConditionInfo<T>> it = infos.iterator(); it.hasNext();) { ConditionInfo<T> conditionInfo = it.next(); if(hasIntersection(conditionInfo.getLockSet(),lockIds)){ return true; } } return false; } private boolean hasIntersection(Set<T> set1,Set<T> set2){ assert set1 != null && set2 != null; for (Iterator<T> it = set2.iterator(); it.hasNext();) { T lockId = it.next(); if(set1.contains(lockId)){ return true; } } return false; } } public class MultStateLock<T> { private ConcurrentMap<Thread,ConditionInfo<T>> condMap = new ConcurrentHashMap<Thread,ConditionInfo<T>>(); private ReentrantLock lock = new ReentrantLock(); final private Condition EMPTY_OBJ = lock.newCondition(); public void lock(Set<T> lockIds) throws InterruptedException{ lock.lock(); boolean isWait = false; try{ if(hasConflict(lockIds)){ isWait = true; Condition c = lock.newCondition(); condMap.put(Thread.currentThread(), new ConditionInfo<T>(lockIds,Thread.currentThread(),c)); c.await(); } if(!isWait){ condMap.put(Thread.currentThread(),new ConditionInfo<T>(lockIds,Thread.currentThread(),EMPTY_OBJ)); } }finally{ lock.unlock(); } } public void unlock(){ lock.lock(); try{ //????map??? ConditionInfo<T> conInfo = condMap.remove(Thread.currentThread()); Set<T> lockIds = conInfo.getLockSet(); //??map????????? Collection<ConditionInfo<T>> infos = condMap.values(); //???????????????,???????????? List<Set<T>> nonConflictSets = new ArrayList<Set<T>>(); List<Condition> nonConflictConds = new ArrayList<Condition>(); for (Iterator<ConditionInfo<T>> it = infos.iterator(); it.hasNext();) { ConditionInfo<T> conditionInfo = (ConditionInfo<T>) it.next(); if(hasIntersection(conditionInfo.getLockSet(),lockIds)&& conditionInfo.getCondition() != EMPTY_OBJ){ if(!hasConflict(nonConflictSets,conditionInfo.getLockSet())){ nonConflictSets.add(conditionInfo.getLockSet()); nonConflictConds.add(conditionInfo.getCondition()); } } } //?????????,???????????? for (Iterator<Condition> it = nonConflictConds.iterator(); it.hasNext();) { Condition cond = it.next(); cond.signal(); } }finally{ lock.unlock(); } } private boolean hasConflict(Collection<Set<T>> collection, Set<T> set){ for (Iterator<Set<T>> it = collection.iterator(); it.hasNext();) { Set<T> setA = it.next(); if(hasIntersection(setA,set)){ return true; } } return false; } private boolean hasConflict(Set<T> lockIds){ Collection<ConditionInfo<T>> infos = condMap.values(); for (Iterator<ConditionInfo<T>> it = infos.iterator(); it.hasNext();) { ConditionInfo<T> conditionInfo = it.next(); if(hasIntersection(conditionInfo.getLockSet(),lockIds)){ return true; } } return false; } private boolean hasIntersection(Set<T> set1,Set<T> set2){ assert set1 != null && set2 != null; for (Iterator<T> it = set2.iterator(); it.hasNext();) { T lockId = it.next(); if(set1.contains(lockId)){ return true; } } return false; } }

17. Deadlock

18. Deadlock(2)

19. Thread Pool ???????? ???ThreadPoolExecutor ??????: ?????????????????????? ?????? ????????????????????? ??????????????????????? ????????????????,?????????????????????? http://www.ibm.com/developerworks/cn/java/j-jtp0730/ http://blog.csdn.net/yangdengfeng2003/archive/2009/04/01/4042250.aspx corePoolSize: ???????????? maximumPoolSize:???????????? unit: ?????????????????? keepAliveTime: ??????????????? workQueue: ??????????? handler: ????????????? http://www.ibm.com/developerworks/cn/java/j-jtp0730/ http://blog.csdn.net/yangdengfeng2003/archive/2009/04/01/4042250.aspx corePoolSize: ???????????? maximumPoolSize:???????????? unit: ?????????????????? keepAliveTime: ??????????????? workQueue: ??????????? handler: ?????????????

20. Thread Pool -Servlet Container ????jconsole??debug??dms??????,?????jconsole???????????jconsole??debug??dms??????,?????jconsole???????

21. Multi-thread Anti-pattern ?????????? ?????? ??????????,????????,?????? ????????wait() ??????????? ?????volatile 1??????????? ???????????????,????????????,????: public class A{      public A(){         this.x=1;         this.y=2;         this.thread=new MyThread();         this.thread.start();      }         }     ????????????????B????A,??java???????,A?????????B???????????,??thread? ????B??????????,?thread????????A??????,????????????????,???B???????????? ??????????????????????????,??????????? ???????????:?A???final,????;???????start????????,??????????? 2??????? ?????????????????synchronized????,synchronized??????,??????,????????? ??????,????????A?????,????????????,??????,????????????????????????: class A{     int x;     public int getX(){        return x;     }     public synchronized void setX(int x)     {        this.x=x;     }   }      x?setter?????,??getter?????,?????????????getX???x?????????,???setX??? ??????,???int???????,???JVM??????,??????????;??,??????int,??double?? long,??getX?setX??????,??double?long??64?,???????????32????(??????jvm?? ?,??jvm???????long?double?read?write????),?????????????????,???????????? volatile???? 3???????????,????????,??????? ?????????,???????: synchronized(array[0])   {      ......      array[0]=new A();      ......   }     ?????array[0]???,???????????array[0]?????????????,????????array[0]??, ???????????array[0]???,????array[0]????,??????????array[0]??,??????????? ???????,???????????????????????,????????final??????????????,???????????? ??? 4?????????wait()? wait?notify????????,??????????????wait?notify,?????????????????????????????????,?????????wait,????if????????: synchronized(lock)   {      if(isEmpty()        lock.wait();         }         ?????????if,?????????????????????notify??notifyAll,????????,????,???? ??????????,???wait()??,??lock??????????????????????,?????????????,?????? ??,?????????????????????????????????,????????????,????????????,????????? ?????????,??????notifyAll,?????????,??????????????(??“???”)????????????? ????????????,?????????????,???????,????????,????,???????? synchronized(lock)   {      while(isEmpty()        lock.wait();         }      ???????????wait??????,?????????notify?????,??????wait??????????????????????? 5????????????? ???????,?????????????;???????,??????????????????????????????????????????,??????Atomic+Atomic!=Atomic? Map map=Collections.synchronizedMap(new HashMap());   if(!map.containsKey("a")){            map.put("a", value);   }     ??????????,map??????,containskey?put?????????,??????????????????????? ????????containsKey?put??,?????????put??a,????????????????,????????????? ???????????,??????????,?????????????????????????? Map map = Collections.synchronizedMap(new HashMap());   synchronized (map) {        if (!map.containsKey("a")) {            map.put("a", value);        }    }     ??,??????,????????????,?????????? Collections.synchronizedMap(new HashMap())?????map??,?????????,????????????ConcurrentHashMap,?? putIfAbsent???????????????????? ????????????,???????new???,???????IO??(?????,webservice?)???????????? ?,?????????,????URLConnection?invoke??URL?????connect timeout?read timeout,????????????????????,????????????,????????????????? 6?????volatile ?jdk5???volatile????,volatile??????????????????????volatile???????jvm spec,????volatile????,??????????????? volatile???????? 1)????,????????????????????: private volatile boolean stopped;   public void close(){      stopped=true;   }     public void run(){        while(!stopped){         //do something      }         }  ???do something???????????volatile??stopped??????,run?????stopped????main memory?????? 2)????,???DLC??? private volatile IoBufferAllocator instance;   public IoBufferAllocator getInsntace(){       if(instance==null){           synchronized (IoBufferAllocator.class) {               if(instance==null)                   instance=new IoBufferAllocator();           }       }       return instance;   }  3)???????? public class CheesyCounter {       private volatile int value;         public int getValue() { return value; }         public synchronized int increment() {           return value++;       }   }   synchronized????????,volatile?????????? volatile???????? 1)???????? public class CheesyCounter {       private volatile int value;         public int getValue() { return value; }         public int increment() {           return value++;       }   }  ??value++???????????:????????,volatile??????????????value????????value?????????????????increment????,????AtomicInteger???? 2)?????????? ????????????????,??????lower< upper? public class NumberRange {       private volatile int lower, upper;         public int getLower() { return lower; }       public int getUpper() { return upper; }         public void setLower(int value) {            if (value > upper)                throw new IllegalArgumentException();           lower = value;       }         public void setUpper(int value) {            if (value < lower)                throw new IllegalArgumentException();           upper = value;       }   }   ???lower?upper???volatile,??setLower?setUpper?????????????????(0,5),? ???setLower(4)?setUpper(3),????????,???????(4,3),???????????????????? setLower?setUpper??: public class NumberRange {       private volatile int lower, upper;         public int getLower() { return lower; }       public int getUpper() { return upper; }         public synchronized void setLower(int value) {            if (value > upper)                throw new IllegalArgumentException();           lower = value;       }         public synchronized void setUpper(int value) {            if (value < lower)                throw new IllegalArgumentException();           upper = value;       }   }  1??????????? ???????????????,????????????,????: public class A{      public A(){         this.x=1;         this.y=2;         this.thread=new MyThread();         this.thread.start();      }         }     ????????????????B????A,??java???????,A?????????B???????????,??thread? ????B??????????,?thread????????A??????,????????????????,???B???????????? ??????????????????????????,??????????? ???????????:?A???final,????;???????start????????,??????????? 2??????? ?????????????????synchronized????,synchronized??????,??????,????????? ??????,????????A?????,????????????,??????,????????????????????????: class A{     int x;     public int getX(){        return x;     }     public synchronized void setX(int x)     {        this.x=x;     }   }      x?setter?????,??getter?????,?????????????getX???x?????????,???setX??? ??????,???int???????,???JVM??????,??????????;??,??????int,??double?? long,??getX?setX??????,??double?long??64?,???????????32????(??????jvm?? ?,??jvm???????long?double?read?write????),?????????????????,???????????? volatile????

22. Concurrent Programming Best Practice ??????????????????? ????????????(??????) ?????????????????? ?????????? ???????????try..finally?? ????????1.5????????? ??volatile???????,?????? ??????????? ??????????? ????InterruptException,??????

23. Q&A

  • Login