• 细数Android开源项目中那些频繁使用的并发库中的类


    这篇blog旨在帮助大家 梳理一下前面分析的那些开源代码中喜欢使用的一些类,这对我们真正理解这些项目是有极大好处的,以后遇到类似问题 我们就可以自己模仿他们也写

    出类似的代码。

    1.ExecutorService

    这个类实际上就是一个接口

    1 public interface ExecutorService extends Executor {

    我们可以看看有哪些频繁使用的类 是实现了这个接口的,其实主要就是3个。

     1  /**
     2      * Creates a thread pool that reuses a fixed number of threads
     3      * operating off a shared unbounded queue.  At any point, at most
     4      * {@code nThreads} threads will be active processing tasks.
     5      * If additional tasks are submitted when all threads are active,
     6      * they will wait in the queue until a thread is available.
     7      * If any thread terminates due to a failure during execution
     8      * prior to shutdown, a new one will take its place if needed to
     9      * execute subsequent tasks.  The threads in the pool will exist
    10      * until it is explicitly {@link ExecutorService#shutdown shutdown}.
    11      *
    12      * @param nThreads the number of threads in the pool
    13      * @return the newly created thread pool
    14      * @throws IllegalArgumentException if {@code nThreads <= 0}
    15      */
    16     public static ExecutorService newFixedThreadPool(int nThreads) {
    17         return new ThreadPoolExecutor(nThreads, nThreads,
    18                                       0L, TimeUnit.MILLISECONDS,
    19                                       new LinkedBlockingQueue<Runnable>());
    20     }

    这个线程池,就是有固定线程数的一个线程池,有共享的无界队列来运行这些线程。

     1 /**
     2      * Creates a thread pool that creates new threads as needed, but
     3      * will reuse previously constructed threads when they are
     4      * available.  These pools will typically improve the performance
     5      * of programs that execute many short-lived asynchronous tasks.
     6      * Calls to {@code execute} will reuse previously constructed
     7      * threads if available. If no existing thread is available, a new
     8      * thread will be created and added to the pool. Threads that have
     9      * not been used for sixty seconds are terminated and removed from
    10      * the cache. Thus, a pool that remains idle for long enough will
    11      * not consume any resources. Note that pools with similar
    12      * properties but different details (for example, timeout parameters)
    13      * may be created using {@link ThreadPoolExecutor} constructors.
    14      *
    15      * @return the newly created thread pool
    16      */
    17     public static ExecutorService newCachedThreadPool() {
    18         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    19                                       60L, TimeUnit.SECONDS,
    20                                       new SynchronousQueue<Runnable>());
    21     }

    这个线程池,是根据需要来创建这些线程的,但是以前构造过的线程 必要时可以重用他们,所以这个在很多android的开源项目里都有用到,很频繁,对于执行很多短期的异步任务来说,这个线程池可以极大的提高程序的性能。

     1 /**
     2      * Creates an Executor that uses a single worker thread operating
     3      * off an unbounded queue. (Note however that if this single
     4      * thread terminates due to a failure during execution prior to
     5      * shutdown, a new one will take its place if needed to execute
     6      * subsequent tasks.)  Tasks are guaranteed to execute
     7      * sequentially, and no more than one task will be active at any
     8      * given time. Unlike the otherwise equivalent
     9      * {@code newFixedThreadPool(1)} the returned executor is
    10      * guaranteed not to be reconfigurable to use additional threads.
    11      *
    12      * @return the newly created single-threaded Executor
    13      */
    14     public static ExecutorService newSingleThreadExecutor() {
    15         return new FinalizableDelegatedExecutorService
    16             (new ThreadPoolExecutor(1, 1,
    17                                     0L, TimeUnit.MILLISECONDS,
    18                                     new LinkedBlockingQueue<Runnable>()));
    19     }

    而这个线程池就比较特殊一点,他只有一个worker线程在工作。

    来看第一个程序:

     1 public class Test1 {
     2 
     3     public static void main(String[] args) {
     4         ExecutorService exectrorService = Executors.newFixedThreadPool(10);
     5         // execute异步的方法去执行这个runnable 但是这种方法无法取得运行之后的返回值
     6         exectrorService.execute(new Runnable() {
     7             @Override
     8             public void run() {
     9                 // TODO Auto-generated method stub
    10                 int i = 0;
    11                 while (true) {
    12                     try {
    13                         Thread.sleep(2000);
    14                     } catch (InterruptedException e) {
    15                         // TODO Auto-generated catch block
    16                         e.printStackTrace();
    17                     }
    18                     System.out.println(i);
    19                     i++;
    20                 }
    21             }
    22 
    23         });
    24 
    25         exectrorService.execute(new Runnable() {
    26             @Override
    27             public void run() {
    28                 // TODO Auto-generated method stub
    29                 int i = 100;
    30                 while (true) {
    31                     try {
    32                         Thread.sleep(2000);
    33                     } catch (InterruptedException e) {
    34                         // TODO Auto-generated catch block
    35                         e.printStackTrace();
    36                     }
    37                     System.out.println(i);
    38                     i++;
    39                 }
    40             }
    41 
    42         });

    很简单 没有什么好说的只是为了演示一下这个方法,继续往下看:

     1 public class Test1 {
     2 
     3     public static void main(String[] args) {
     4         ExecutorService exectrorService = Executors.newFixedThreadPool(10);
     5         Future future = exectrorService.submit(new Runnable() {
     6 
     7             @Override
     8             public void run() {
     9                 System.out.println("thread start");
    10                 // TODO Auto-generated method stub
    11                 try {
    12                     Thread.sleep(13000);
    13                 } catch (InterruptedException e) {
    14                     // TODO Auto-generated catch block
    15                     e.printStackTrace();
    16                 }
    17                 System.out.println("task done");
    18             }
    19         });
    20         System.out.println("ready to print status");
    21         try {
    22             // 执行完毕以后才会返回null,如果线程还没有执行完毕 那这个地方会阻塞
    23             System.out.println("future.get ==" + future.get());
    24         } catch (InterruptedException e) {
    25             // TODO Auto-generated catch block
    26             e.printStackTrace();
    27         } catch (ExecutionException e) {
    28             // TODO Auto-generated catch block
    29             e.printStackTrace();
    30         }
    31         System.out.println("finish ready");

    这个就是为了演示get方法是个阻塞方法的 我们可以看下打印的日志。

    程序一开始运行 日志如下:

    thread start
    ready to print status

    当线程执行完毕大约过了13秒以后

    才会继续输入日志如下:

    task done
    future.get ==null
    finish ready

    继续看下面的例子:

     1 package com.android.testclass;
     2 
     3 import java.util.concurrent.Callable;
     4 import java.util.concurrent.ExecutionException;
     5 import java.util.concurrent.ExecutorService;
     6 import java.util.concurrent.Executors;
     7 import java.util.concurrent.Future;
     8 
     9 public class Test1 {
    10 
    11     public static void main(String[] args) {
    12         ExecutorService exectrorService = Executors.newFixedThreadPool(10);
    13         // 这个submit方法则会保证结束以后把结果返回给future,用泛型定义的方法 你可以
    14         // 用任意的object代替T
    15         Future future = exectrorService.submit(new Callable<String>() {
    16             @Override
    17             public String call() throws Exception {
    18                 // TODO Auto-generated method stub
    19                 System.out.println("call start");
    20 
    21                 Thread.sleep(5000);
    22 
    23                 return "call done";
    24             }
    25         });
    26         System.out.println("ready to print");
    27         try {
    28             System.out.println("future.get()" + future.get());
    29         } catch (InterruptedException e) {
    30             // TODO Auto-generated catch block
    31             e.printStackTrace();
    32         } catch (ExecutionException e) {
    33             // TODO Auto-generated catch block
    34             e.printStackTrace();
    35         }
    36         System.out.println("finish");
    37 
    38     }
    39 }

    同样是submit方法 只不过这次我们换了一个参数 这次是callable参数,这么做的好处就是执行完毕以后可以拿到结果了

    一开始输出:

    call start
    ready to print

    线程执行完毕以后输出:

    future.get()call done
    finish

    然后我们继续看invokeany这个函数:

     1 package com.android.testclass;
     2 
     3 import java.util.HashSet;
     4 import java.util.Set;
     5 import java.util.concurrent.Callable;
     6 import java.util.concurrent.ExecutionException;
     7 import java.util.concurrent.ExecutorService;
     8 import java.util.concurrent.Executors;
     9 
    10 public class Test2 {
    11 
    12     public static void main(String[] args) {
    13         ExecutorService executorService = Executors.newFixedThreadPool(10);
    14         Set<Callable<String>> callables = new HashSet<Callable<String>>();
    15         callables.add(new Callable<String>() {
    16             @Override
    17             public String call() throws Exception {
    18                 // TODO Auto-generated method stub
    19                 System.out.println("task 1 start");
    20                 Thread.sleep(3000);
    21                 return "Task 1";
    22             }
    23         });
    24         callables.add(new Callable<String>() {
    25             @Override
    26             public String call() throws Exception {
    27                 System.out.println("task 2 start");
    28                 Thread.sleep(3000);
    29                 return "Task 2";
    30             }
    31         });
    32         callables.add(new Callable<String>() {
    33             @Override
    34             public String call() throws Exception {
    35                 System.out.println("task 3 start");
    36                 Thread.sleep(3000);
    37                 return "Task 3";
    38             }
    39         });
    40         System.out.println("ready to print");
    41         try {
    42             //返回某一个callable执行结束的结果,结果并不确定
    43             String result = executorService.invokeAny(callables);
    44             System.out.println("result==" + result);
    45         } catch (InterruptedException e) {
    46             // TODO Auto-generated catch block
    47             e.printStackTrace();
    48         } catch (ExecutionException e) {
    49             // TODO Auto-generated catch block
    50             e.printStackTrace();
    51         }
    52         System.out.println("done to print");
    53 
    54     }
    55 }

    输出我就不放了 大家可以自己跑一下。这个函数用的比较少。

    那下面这个invokeall函数用的就比较多了

     1 package com.android.testclass;
     2 
     3 import java.util.HashSet;
     4 import java.util.List;
     5 import java.util.Set;
     6 import java.util.concurrent.Callable;
     7 import java.util.concurrent.ExecutionException;
     8 import java.util.concurrent.ExecutorService;
     9 import java.util.concurrent.Executors;
    10 import java.util.concurrent.Future;
    11 
    12 public class Test3 {
    13 
    14     public static void main(String[] args) {
    15         ExecutorService executorService = Executors.newFixedThreadPool(10);
    16         Set<Callable<String>> callables = new HashSet<Callable<String>>();
    17         callables.add(new Callable<String>() {
    18             @Override
    19             public String call() throws Exception {
    20                 // TODO Auto-generated method stub
    21                 System.out.println("task 1 start");
    22                 Thread.sleep(3000);
    23                 return "Task 1";
    24             }
    25         });
    26         callables.add(new Callable<String>() {
    27             @Override
    28             public String call() throws Exception {
    29                 System.out.println("task 2 start");
    30                 Thread.sleep(6000);
    31                 return "Task 2";
    32             }
    33         });
    34         callables.add(new Callable<String>() {
    35             @Override
    36             public String call() throws Exception {
    37                 System.out.println("task 3 start");
    38                 Thread.sleep(9000);
    39                 return "Task 3";
    40             }
    41         });
    42         System.out.println("ready to print");
    43 
    44         try {
    45             // invoke方法也是阻塞方法,一定是所有callable都执行完毕才会返回结果
    46             List<Future<String>> futures = executorService.invokeAll(callables);
    47             System.out.println("invoke done");
    48             for (Future<String> future : futures) {
    49                 System.out.println("future.get=" + future.get());
    50                 System.out.println("get done");
    51             }
    52             System.out.println("all get done");
    53         } catch (InterruptedException e) {
    54             // TODO Auto-generated catch block
    55             e.printStackTrace();
    56         } catch (ExecutionException e) {
    57             // TODO Auto-generated catch block
    58             e.printStackTrace();
    59         }
    60 
    61     }
    62 }

    总的来说,在android里如果你要使用线程池的话,那上面的这些方法 基本就肯定足够你使用了。

    2.ConcurrentHashMap

    这个类,相信很多人都不陌生,我就略微提一下,很多人以前在单线程的时候使用hashmap,多线程的时候使用hashtable,这么做虽然是对的,

    但是hashtable里的源码说明了 这是直接对整个map进行加锁,效率是很低的,而这个concurrenthashmap的读操作几乎不会有锁,

    而写操作由于采用了分段处理,所以写操作的锁 的概率和次数也大大降低。总体来说这是一个效率极高的 可适用于并发性的hashmap。

    例子和原理 网上有很多 我这里就不放了。

    此外和他类似的还有LinkedHashMap,实现LRU的最好选择,这个也不多讲,只是提一下,网上资料很多。

    3.PriorityBlockingQueue

    这个就是优先级队列,当然也是支持并发的,这个队列里存放的对象 必须是实现了Comparable 接口的。并且小的是在这个队列前面的 大的就一定是在队列的后面。

    比如说我们先定义一个类:

     1 package com.android.testclass;
     2 
     3 public class PriorityEntity implements Comparable<PriorityEntity> {
     4 
     5     private static int count = 0;
     6     private int id = count++;
     7     private int priority;
     8     private int index = 0;
     9 
    10     public PriorityEntity(int priority, int index) {
    11         // TODO Auto-generated constructor stub
    12         this.priority = priority;
    13         this.index = index;
    14     }
    15 
    16     @Override
    17     public String toString() {
    18         return "PriorityEntity [id=" + id + ", priority=" + priority + ", index=" + index + "]";
    19     }
    20 
    21     @Override
    22     public int compareTo(PriorityEntity o) {
    23         // TODO Auto-generated method stub
    24         return this.priority > o.priority ? 1 : this.priority < o.priority ? -1 : 0;
    25     }
    26 
    27 }

    那个静态变量就表示索引的,构造出一个对象 索引就加1. 然后我们来写一下测试这个队列的代码:

     1 package com.android.testclass;
     2 
     3 import java.util.Random;
     4 import java.util.concurrent.ExecutorService;
     5 import java.util.concurrent.Executors;
     6 import java.util.concurrent.PriorityBlockingQueue;
     7 import java.util.concurrent.TimeUnit;
     8 
     9 public class Test6 {
    10 
    11     public static void main(String[] args) {
    12         // TODO Auto-generated method stub
    13 
    14         PriorityBlockingQueue q = new PriorityBlockingQueue<>();
    15         Random r = new Random(47);
    16         ExecutorService se = Executors.newCachedThreadPool();
    17         //往队列里 放对象,priority的值是 随即的
    18         se.execute(new Runnable() {
    19 
    20             @Override
    21             public void run() {
    22                 // TODO Auto-generated method stub
    23                 int i = 0;
    24                 while (true) {
    25                     q.put(new PriorityEntity(r.nextInt(10), i++));
    26 
    27                     try {
    28                         TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
    29                     } catch (InterruptedException e) {
    30                         // TODO Auto-generated catch block
    31                         e.printStackTrace();
    32                     }
    33 
    34                 }
    35             }
    36         });
    37         //从队列里 取对象,然后把队列里剩余的值打出来 就会发现 每次取出来的都是最小的那个 剩下的都是从小到大排序好的
    38         se.execute(new Runnable() {
    39 
    40             @Override
    41             public void run() {
    42                 // TODO Auto-generated method stub
    43                 while (true) {
    44                     try {
    45                         System.out.println(("take-- " + q.take() + " left:-- [" + q.toString() + "]"));
    46                     } catch (InterruptedException e1) {
    47                         // TODO Auto-generated catch block
    48                         e1.printStackTrace();
    49                     }
    50                     try {
    51                         TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
    52                     } catch (InterruptedException e) {
    53                         // TODO Auto-generated catch block
    54                         e.printStackTrace();
    55                     }
    56 
    57                 }
    58             }
    59         });
    60 
    61     }
    62 
    63 }

    截取一段日志 可以得到我们注释里的结论:

     1 take-- PriorityEntity  [priority=8, index=0] left:-- [[]]
     2 take-- PriorityEntity  [priority=1, index=1] left:-- [[]]
     3 take-- PriorityEntity  [priority=8, index=2] left:-- [[]]
     4 take-- PriorityEntity  [priority=7, index=3] left:-- [[PriorityEntity  [priority=8, index=4]]]
     5 take-- PriorityEntity  [priority=8, index=4] left:-- [[PriorityEntity  [priority=9, index=5]]]
     6 take-- PriorityEntity  [priority=1, index=6] left:-- [[PriorityEntity  [priority=8, index=7], PriorityEntity  [priority=9, index=5]]]
     7 take-- PriorityEntity  [priority=8, index=7] left:-- [[PriorityEntity  [priority=9, index=5]]]
     8 take-- PriorityEntity  [priority=2, index=8] left:-- [[PriorityEntity  [priority=9, index=5]]]
     9 take-- PriorityEntity  [priority=9, index=5] left:-- [[]]
    10 take-- PriorityEntity  [priority=5, index=9] left:-- [[]]
    11 take-- PriorityEntity  [priority=4, index=10] left:-- [[]]
    12 take-- PriorityEntity  [priority=4, index=13] left:-- [[PriorityEntity  [priority=6, index=11], PriorityEntity  [priority=6, index=12]]]
    13 take-- PriorityEntity  [priority=3, index=14] left:-- [[PriorityEntity  [priority=6, index=16], PriorityEntity  [priority=6, index=12], PriorityEntity  [priority=6, index=11], PriorityEntity  [priority=8, index=15]]]
    14 take-- PriorityEntity  [priority=6, index=16] left:-- [[PriorityEntity  [priority=6, index=12], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=6, index=11]]]
    15 take-- PriorityEntity  [priority=6, index=12] left:-- [[PriorityEntity  [priority=6, index=17], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=6, index=11]]]
    16 take-- PriorityEntity  [priority=6, index=17] left:-- [[PriorityEntity  [priority=6, index=11], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=18]]]
    17 take-- PriorityEntity  [priority=6, index=11] left:-- [[PriorityEntity  [priority=8, index=18], PriorityEntity  [priority=8, index=15]]]
    18 take-- PriorityEntity  [priority=4, index=19] left:-- [[PriorityEntity  [priority=8, index=18], PriorityEntity  [priority=8, index=15]]]
    19 take-- PriorityEntity  [priority=8, index=18] left:-- [[PriorityEntity  [priority=8, index=15]]]
    20 take-- PriorityEntity  [priority=7, index=20] left:-- [[PriorityEntity  [priority=8, index=15]]]
    21 take-- PriorityEntity  [priority=2, index=21] left:-- [[PriorityEntity  [priority=4, index=22], PriorityEntity  [priority=8, index=15]]]
    22 take-- PriorityEntity  [priority=4, index=22] left:-- [[PriorityEntity  [priority=8, index=23], PriorityEntity  [priority=8, index=15]]]
    23 take-- PriorityEntity  [priority=8, index=23] left:-- [[PriorityEntity  [priority=8, index=15]]]
    24 take-- PriorityEntity  [priority=5, index=24] left:-- [[PriorityEntity  [priority=8, index=15]]]
    25 take-- PriorityEntity  [priority=2, index=25] left:-- [[PriorityEntity  [priority=8, index=26], PriorityEntity  [priority=8, index=15]]]
    26 take-- PriorityEntity  [priority=3, index=27] left:-- [[PriorityEntity  [priority=4, index=28], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=26]]]
    27 take-- PriorityEntity  [priority=1, index=30] left:-- [[PriorityEntity  [priority=4, index=28], PriorityEntity  [priority=7, index=29], PriorityEntity  [priority=8, index=26], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=31]]]
    28 take-- PriorityEntity  [priority=4, index=28] left:-- [[PriorityEntity  [priority=7, index=29], PriorityEntity  [priority=8, index=15], PriorityEntity  [priority=8, index=26], PriorityEntity  [priority=9, index=32], PriorityEntity  [priority=8, index=31]]]

    有兴趣的话可以看看java里面 有几种类 都实现了AbstractQueue,可以挑选出适合自己业务里的队列,减少开发难度

    1 public abstract class AbstractQueue<E>
    2     extends AbstractCollection<E>
    3     implements Queue<E> {

    4.CopyOnWriteArrayList

    考虑这样一种场景,一个list,被好几个线程同时读写,那一般都会报错。

     1 Exception in thread "pool-1-thread-7" java.util.ConcurrentModificationException
     2     at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
     3     at java.util.ArrayList$Itr.next(Unknown Source)
     4     at com.android.testclass.Test7$ReadTask.run(Test7.java:35)
     5     at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
     6     at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
     7     at java.lang.Thread.run(Unknown Source)
     8 Exception in thread "pool-1-thread-6" java.util.ConcurrentModificationException
     9     at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    10     at java.util.ArrayList$Itr.next(Unknown Source)
    11     at com.android.testclass.Test7$ReadTask.run(Test7.java:35)
    12     at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    13     at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    14     at java.lang.Thread.run(Unknown Source)

    于是很多人就喜欢用Collections.synchronizedList() 来处理,但是这样做在很多时候效率是低的,比如

    假设现在告诉你,你需要设计一个缓存list,你就应该使用CopyOnWrite这个类了,因为缓存大家都知道,读操作比较多,而写操作除了在初始建立缓存的阶段,其他时候很少使用。

    他的原理也很简单,就是你在用迭代器写操作的时候 是把原来的数据拷贝了一份镜像在内存中,而你在读的时候 是读的本体,写操作写完以后才会覆盖掉原来的本地。所以可以

    得知 这个类对于频繁读的同步性list 是非常有效的。使用方法也很简单。

    1         List<String> list = new CopyOnWriteArrayList<String>();

    5.ThreadLocal

    这个类也是很有效,很多开源作者喜欢用的一个类,他主要的作用是为每个线程创造一个变量的副本互相不会影响。很多人不理解这句话,

    对于多线程操作来说 分为两种

    1 第一种,线程和线程之间互相读取操作,比如全局的计数器这种,a线程要加,b线程也要加,每次加的时候 都要读取最新的计数器的状态。这是最常见的一种同步操作。

    2 第二种,session,session一个用户一个,互相不影响,大家维持自己的就可以,他的目标就是a的seesion a自己操作 保存 读取,b的seesion也是自己维护,和其他人无关。

    换一句话说 如果你需要多个线程之间通信,那就用同步机制,

    如果你不需要线程与线程之间通信,只要互相别影响 不让他们发生冲突 则threadlocal是最佳选择。

     1 package com.android.testclass;
     2 
     3 public class Test8 {
     4 
     5     static final ThreadLocal<Integer> local = new ThreadLocal<Integer>() {
     6 
     7         protected Integer initialValue() {
     8 
     9             return 0;
    10         };
    11 
    12     };
    13 
    14     public static void main(String[] args) {
    15         // TODO Auto-generated method stub
    16 
    17         Thread[] threads = new Thread[5];
    18         for (int i = 0; i < 5; i++) {
    19             threads[i] = new Thread(new Runnable() {
    20 
    21                 @Override
    22                 public void run() {
    23                     // TODO Auto-generated method stub
    24 
    25                     int num = local.get();
    26                     for (int i = 0; i < 5; i++) {
    27                         num++;
    28                     }
    29                     local.set(num);
    30                     System.out.println(Thread.currentThread().getName() + " : " + local.get());
    31 
    32                 }
    33             }, "thread-" + i);
    34         }
    35 
    36         for (Thread thread : threads) {
    37             thread.start();
    38         }
    39 
    40     }
    41 
    42 }

    看下输出

    1 thread-0 : 5
    2 thread-4 : 5
    3 thread-1 : 5
    4 thread-3 : 5
    5 thread-2 : 5

    接着看下面的

     1 package com.android.testclass;
     2 
     3 public class Test9 {
     4 
     5     private static Index num = new Index();
     6     // 创建一个Index类型的本地变量
     7     private static ThreadLocal<Index> local = new ThreadLocal<Index>() {
     8         @Override
     9         protected Index initialValue() {
    10             return num;
    11         }
    12     };
    13 
    14     public static void main(String[] args) throws InterruptedException {
    15         Thread[] threads = new Thread[5];
    16         for (int j = 0; j < 5; j++) {
    17             threads[j] = new Thread(new Runnable() {
    18                 @Override
    19                 public void run() {
    20                     // 取出当前线程的本地变量,并累加1000次
    21                     Index index = local.get();
    22                     for (int i = 0; i < 1000; i++) {
    23                         index.increase();
    24                     }
    25                     System.out.println(Thread.currentThread().getName() + " : " + index.num);
    26 
    27                 }
    28             }, "Thread-" + j);
    29         }
    30         for (Thread thread : threads) {
    31             thread.start();
    32         }
    33     }
    34 
    35     static class Index {
    36         int num;
    37 
    38         public void increase() {
    39             num++;
    40         }
    41     }
    42 
    43 }

    看输出

    Thread-1 : 2594
    Thread-4 : 3594
    Thread-2 : 2594
    Thread-0 : 2594
    Thread-3 : 4594

    是因为第10行,那边放的是一个静态变量的引用,所以输出的结果不是我们想象的

    其实只要改成

    1 private static ThreadLocal<Index> local = new ThreadLocal<Index>() {
    2         @Override
    3         protected Index initialValue() {
    4             return new Index();
    5         }
    6     };

    结果就是正确的:

    1 Thread-2 : 1000
    2 Thread-3 : 1000
    3 Thread-0 : 1000
    4 Thread-4 : 1000
    5 Thread-1 : 1000
  • 相关阅读:
    Android中获取屏幕高度和宽度
    Android--第三方控件--okHttp
    Android中获取手机电量信息
    Android中获取并设置屏幕亮度
    ViewPager实现图片的轮播
    ScrollView嵌套使用ListView冲突的解决与分析
    Vue中的MVVM框架
    vue(一)
    RabbitMQ消费端ACK与重回队列机制,TTL,死信队列详解(十一)
    RabbitMQ消费端限流策略(十)
  • 原文地址:https://www.cnblogs.com/punkisnotdead/p/4795579.html
Copyright © 2020-2023  润新知