线程安全策略

不可变对象

不可变对象需要满足的条件:

1. 对象创建以后其状态就不能修改

2. 对象所有域都是final类型

3. 对象时正确创建的(在对象创建期间,this引用没有逸出)

一个类的private方法会被隐式地指定为final方法

 1 package com.mmall.concurrency.demo.immutable;
 2 
 3 import com.google.common.collect.Maps;
 4 import com.mmall.concurrency.annoations.NotThreadSafe;
 5 
 6 import java.util.Map;
 7 /*
 8 除了final之外,还可以通过
 9 Collections.unmodifiableXXX:Collection、List、Set、Map...定义不可变对象
10 还有guava里面的ImmutableXXX:Collection、List、Set、Map...
11  */
12 @NotThreadSafe
13 public class ImmutableDemo1 {
14     //一旦被赋值就不能被修改
15     private final static Integer a = 1;
16     private final static String b = "2";
17     /*
18     final——>map——>[......]
19     只是final修饰的map不能在初始化后指向其他对象,但是map容器里面的内容可以改变
20      */
21     private final static Map<Integer, Integer> map = Maps.newHashMap();
22 
23     static {
24         map.put(1, 2);
25         map.put(3, 4);
26         map.put(5, 6);
27     }
28 
29     public static void main(String[] args) {
30         /*
31         都会编译报错
32         a = 2;
33         b = "3";
34         map = Maps.newHashMap();
35         */
36         map.put(1, 3);
37         System.out.println(map.get(1));//3
38     }
39 
40     private void fun(final int a) {
41 //        a = 1;
42     }
43 }
 1 package com.mmall.concurrency.demo.immutable;
 2 
 3 import com.google.common.collect.Maps;
 4 import com.mmall.concurrency.annoations.ThreadSafe;
 5 
 6 import java.util.Collections;
 7 import java.util.Map;
 8 
 9 @ThreadSafe
10 public class ImmutableDemo2 {
11     private static Map<Integer, Integer> map = Maps.newHashMap();
12 
13     static {
14         map.put(1, 2);
15         map.put(3, 4);
16         map.put(5, 6);
17         map = Collections.unmodifiableMap(map);
18     }
19 
20     public static void main(String[] args) {
21         map.put(1, 3);
22         System.out.println(map.get(1));
23     }
24 
25 }
26 /*
27 Exception in thread "main" java.lang.UnsupportedOperationException
28     at java.util.Collections$UnmodifiableMap.put(Collections.java:1457)
29     at com.mmall.concurrency.demo.immutable.ImmutableDemo2.main(ImmutableDemo2.java:21)
30  */
 1 package com.mmall.concurrency.demo.immutable;
 2 
 3 import com.google.common.collect.ImmutableList;
 4 import com.google.common.collect.ImmutableMap;
 5 import com.google.common.collect.ImmutableSet;
 6 import com.mmall.concurrency.annoations.ThreadSafe;
 7 
 8 @ThreadSafe
 9 public class ImmutableDemo3 {
10     private final static ImmutableList<Integer> list = ImmutableList.of(1, 2, 3);
11 
12     private final static ImmutableSet set = ImmutableSet.copyOf(list);
13 
14     private final static ImmutableMap<Integer, Integer> map = ImmutableMap.of(1, 2, 3, 4);
15 
16     private final static ImmutableMap<Integer, Integer> map2 = ImmutableMap.<Integer, Integer>builder()
17             .put(1, 2).put(3, 4).put(5, 6).build();
18 
19     public static void main(String[] args) {
20         System.out.println(map2.get(3));//4
21     }
22 }

线程封闭

Ad-hoc线程封闭:程序控制实现,最糟糕,忽略

堆栈封闭:局部变量,无并发问题

ThreadLocal线程封闭:内部维护了一个Map,Key是当前线程的名称,Value是封闭的对象

线程不安全类与写法

StringBuffer与StringBuilder

 1 package com.mmall.concurrency.demo.commonUnsafe;
 2 
 3 import com.mmall.concurrency.annoations.NotThreadSafe;
 4 
 5 import java.util.concurrent.CountDownLatch;
 6 import java.util.concurrent.ExecutorService;
 7 import java.util.concurrent.Executors;
 8 import java.util.concurrent.Semaphore;
 9 
10 @NotThreadSafe
11 public class StringDemo1 {
12     public static int clientTotal = 5000;
13     public static int threadTotal = 200;
14     public static StringBuilder sb = new StringBuilder();
15 
16     public static void main(String[] args) throws Exception {
17         ExecutorService executorService = Executors.newCachedThreadPool();
18         final Semaphore semaphore = new Semaphore(threadTotal);
19         final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
20         for (int i = 0; i < clientTotal ; i++) {
21             executorService.execute(() -> {
22                 try {
23                     semaphore.acquire();
24                     update();
25                     semaphore.release();
26                 } catch (Exception e) {
27                    e.printStackTrace();
28                 }
29                 countDownLatch.countDown();
30             });
31         }
32         countDownLatch.await();
33         executorService.shutdown();
34         /*
35         4982
36         把StringBuilder改为StringBuffer结果为5000
37          */
38         System.out.println(sb.length());
39     }
40 
41     private static void update() {
42         sb.append("1");
43     }
44 }

SimpleDateFormat与JodaTime

 1 package com.mmall.concurrency.demo.commonUnsafe;
 2 
 3 import com.mmall.concurrency.annoations.ThreadSafe;
 4 
 5 import java.text.SimpleDateFormat;
 6 import java.util.concurrent.CountDownLatch;
 7 import java.util.concurrent.ExecutorService;
 8 import java.util.concurrent.Executors;
 9 import java.util.concurrent.Semaphore;
10 
11 @ThreadSafe
12 public class DateFormatDemo1 {
13     public static int clientTotal = 5000;
14     public static int threadTotal = 200;
15 
16     public static void main(String[] args) throws Exception {
17         ExecutorService executorService = Executors.newCachedThreadPool();
18         final Semaphore semaphore = new Semaphore(threadTotal);
19         final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
20         for (int i = 0; i < clientTotal ; i++) {
21             executorService.execute(() -> {
22                 try {
23                     semaphore.acquire();
24                     update();
25                     semaphore.release();
26                 } catch (Exception e) {
27                     e.printStackTrace();
28                 }
29                 countDownLatch.countDown();
30             });
31         }
32         countDownLatch.await();
33         executorService.shutdown();
34     }
35 
36     /*
37     通过堆栈封闭实现SimpleDateFormat的线程安全
38      */
39     private static void update() {
40         try {
41             SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
42             System.out.println(simpleDateFormat.parse("2018-02-08"));
43         } catch (Exception e) {
44             e.printStackTrace();
45         }
46     }
47 }
 1 package com.mmall.concurrency.demo.commonUnsafe;
 2 
 3 import com.mmall.concurrency.annoations.ThreadSafe;
 4 import org.joda.time.DateTime;
 5 import org.joda.time.format.DateTimeFormat;
 6 import org.joda.time.format.DateTimeFormatter;
 7 
 8 import java.util.concurrent.CountDownLatch;
 9 import java.util.concurrent.ExecutorService;
10 import java.util.concurrent.Executors;
11 import java.util.concurrent.Semaphore;
12 
13 @ThreadSafe
14 public class DateFormatDemo2 {
15     public static int clientTotal = 5000;
16     public static int threadTotal = 200;
17     private static DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd");
18 
19     public static void main(String[] args) throws Exception {
20         ExecutorService executorService = Executors.newCachedThreadPool();
21         final Semaphore semaphore = new Semaphore(threadTotal);
22         final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
23         for (int i = 0; i < clientTotal; i++) {
24             final int count = i;
25             executorService.execute(() -> {
26                 try {
27                     semaphore.acquire();
28                     update(count);
29                     semaphore.release();
30                 } catch (Exception e) {
31                     e.printStackTrace();
32                 }
33                 countDownLatch.countDown();
34             });
35         }
36         countDownLatch.await();
37         executorService.shutdown();
38     }
39 
40     private static void update(int i) {
41         System.out.println(i+" "+DateTime.parse("2018-02-08", dateTimeFormatter).toDate());
42     }
43 }

ArrayList、HashSet及HashMap

 1 package com.mmall.concurrency.demo.commonUnsafe;
 2 
 3 import com.mmall.concurrency.annoations.NotThreadSafe;
 4 
 5 import java.util.ArrayList;
 6 import java.util.List;
 7 import java.util.concurrent.CountDownLatch;
 8 import java.util.concurrent.ExecutorService;
 9 import java.util.concurrent.Executors;
10 import java.util.concurrent.Semaphore;
11 
12 @NotThreadSafe
13 public class ArrayListDemo1 {
14     public static int clientTotal = 5000;
15     public static int threadTotal = 200;
16     private static List<Integer> list = new ArrayList<>();
17 
18     public static void main(String[] args) throws Exception {
19         ExecutorService executorService = Executors.newCachedThreadPool();
20         final Semaphore semaphore = new Semaphore(threadTotal);
21         final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
22         for (int i = 0; i < clientTotal; i++) {
23             final int count = i;
24             executorService.execute(() -> {
25                 try {
26                     semaphore.acquire();
27                     update(count);
28                     semaphore.release();
29                 } catch (Exception e) {
30                     e.printStackTrace();
31                 }
32                 countDownLatch.countDown();
33             });
34         }
35         countDownLatch.await();
36         executorService.shutdown();
37         System.out.println(list.size());//4978
38     }
39 
40     private static void update(int i) {
41         list.add(i);
42     }
43 }

同步容器

Vector、Stack及Hashtable

Collections.synchronizedXXX...

 1 package com.mmall.concurrency.demo.syncContainer;
 2 
 3 import com.mmall.concurrency.annoations.ThreadSafe;
 4 
 5 import java.util.List;
 6 import java.util.Vector;
 7 import java.util.concurrent.CountDownLatch;
 8 import java.util.concurrent.ExecutorService;
 9 import java.util.concurrent.Executors;
10 import java.util.concurrent.Semaphore;
11 
12 @ThreadSafe
13 public class VectorDemo1 {
14     public static int clientTotal = 5000;
15     public static int threadTotal = 200;
16     private static List<Integer> list = new Vector<>();
17 
18     public static void main(String[] args) throws Exception {
19         ExecutorService executorService = Executors.newCachedThreadPool();
20         final Semaphore semaphore = new Semaphore(threadTotal);
21         final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
22         for (int i = 0; i < clientTotal; i++) {
23             final int count = i;
24             executorService.execute(() -> {
25                 try {
26                     semaphore.acquire();
27                     update(count);
28                     semaphore.release();
29                 } catch (Exception e) {
30                     e.printStackTrace();
31                 }
32                 countDownLatch.countDown();
33             });
34         }
35         countDownLatch.await();
36         executorService.shutdown();
37         System.out.println(list.size());//5000
38     }
39 
40     private static void update(int i) {
41         list.add(i);
42     }
43 }
 1 package com.mmall.concurrency.demo.syncContainer;
 2 
 3 import com.mmall.concurrency.annoations.NotThreadSafe;
 4 
 5 import java.util.Vector;
 6 
 7 //ArrayIndexOutOfBoundsException
 8 @NotThreadSafe
 9 public class VectorDemo2 {
10     private static Vector<Integer> vector = new Vector<>();
11     public static void main(String[] args) {
12         while(true){
13             for (int i = 0; i < 10; i++)
14                 vector.add(i);
15             new Thread(new Runnable() {
16                 @Override
17                 public void run() {
18                     for (int i = 0; i < vector.size(); i++)
19                         vector.remove(i);
20                 }
21             }).start();
22 
23             new Thread(new Runnable() {
24                 @Override
25                 public void run() {
26                     for (int i = 0; i < vector.size(); i++)
27                         vector.get(i);
28                 }
29             }).start();
30         }
31 
32     }
33 }
 1 package com.mmall.concurrency.demo.syncContainer;
 2 
 3 import java.util.Iterator;
 4 import java.util.Vector;
 5 
 6 public class VectorDemo3 {
 7     //java.util.ConcurrentModificationException
 8     private static void fun1(Vector<Integer> v1) { //foreach
 9         for(Integer i : v1) {
10             if (i.equals(3)) {
11                 v1.remove(i);
12             }
13         }
14     }
15 
16     //java.util.ConcurrentModificationException
17     private static void fun2(Vector<Integer> v1) { //iterator
18         Iterator<Integer> iterator = v1.iterator();
19         while (iterator.hasNext()) {
20             Integer i = iterator.next();
21             if (i.equals(3)) {
22                 v1.remove(i);
23             }
24         }
25     }
26 
27     //success
28     private static void fun3(Vector<Integer> v1) { //for
29         for (int i = 0; i < v1.size(); i++) {
30             if (v1.get(i).equals(3)) {
31                 v1.remove(i);
32             }
33         }
34     }
35 
36     public static void main(String[] args) {
37         Vector<Integer> vector = new Vector<>();
38         vector.add(1);
39         vector.add(2);
40         vector.add(3);
41         fun3(vector);
42     }
43 }

并发容器

ArrayList——CopyOnWriteArrayList

缺点:耗内存,可能会出现Minor GC和Full GC

不能用于实时读的场景

HashSet、TreeSet——CopyOnWriteArraySet、ConcurrentSkipListSet

HashMap、TreeMap——ConcurrentHashMap、ConcurrentSkipListMap

安全共享对象策略小结

1. 线程限制:一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改

2. 共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是任何线程都不能修改它

3. 线程安全对象:一个线程安全的对象或者容器,在内部通过同步机制来保证线程安全,所以其他线程无需额外的同步就可以通过公共接口随意访问它

4. 被守护对象:被守护对象只能通过获取特定的锁来访问

posted @ 2018-04-10 19:15  sakura1027  阅读(349)  评论(0编辑  收藏  举报