线程安全策略
不可变对象
不可变对象需要满足的条件:
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. 被守护对象:被守护对象只能通过获取特定的锁来访问