不可变对象
不可变对象需要满足的条件
对象创建以后其状态就不能修改
对象所有域都是final类型
对象是正确创建的(在对象创建期间,this引用没有逸出)
创建不可变对象的方式(参考String类型)
将类声明成final类型,使其不可以被继承
将所有的成员设置成私有的,使其他的类和对象不能直接访问这些成员
对变量不提供set方法
将所有可变的成员声明为final,这样只能对他们赋值一次
通过构造器初始化所有成员,进行深度拷贝
在get方法中,不直接返回对象本身,而是克隆对象,返回对象的拷贝
final关键字:类、方法、变量
修饰类:不能被继承(final类中的所有方法都会被隐式的声明为final方法)
修饰方法:1、锁定方法不被继承类修改;2、提升效率(private方法被隐式修饰为final方法)
修饰变量:基本数据类型变量(初始化之后不能修改)、引用类型变量(初始化之后不能再修改其引用)
其他的不可变对象的创建
Collections.unmodifiableMap
创建完以后不允许被修改源码
/**
*初始化的时候将传进来的map赋值给一个final类型的map,然后将所有会修改的方法直接抛出UnsupportedOperationException异常
* Returns anunmodifiable view of the specified map. This method
* allowsmodules to provide users with "read-only" access to internal
* maps. Query operations on the returned map"read through"
* to thespecified map, and attempts to modify the returned
* map, whetherdirect or via its collection views, result in an
*UnsupportedOperationException.
*
* The returnedmap will be serializable if the specified map
* isserializable.
*
* @param the class of the map keys
* @param the class of the map values
* @param m the map for which an unmodifiable view isto be returned.
* @return anunmodifiable view of the specified map.
*/
public static Map unmodifiableMap(Mapm) {
return newUnmodifiableMap<>(m);
}
/**
* @serialinclude
*/
private static classUnmodifiableMap implements Map, Serializable {
private staticfinal long serialVersionUID = -1034234728574286014L;
private finalMap m;
UnmodifiableMap(Map m) {
if (m==null)
thrownew NullPointerException();
this.m= m;
}
public int size() {return m.size();}
public booleanisEmpty() {returnm.isEmpty();}
public booleancontainsKey(Object key) {returnm.containsKey(key);}
public booleancontainsValue(Object val) {return m.containsValue(val);}
public V get(Objectkey) {return m.get(key);}
public V put(Kkey, V value) {
throw newUnsupportedOperationException();
}
public V remove(Objectkey) {
throw newUnsupportedOperationException();
}
测试
@ThreadSafe
public class ImmutableExample1 {
private staticMap map = Maps.newHashMap();
static {
map.put(1,2);
map =Collections.unmodifiableMap(map);
}
public static voidmain(String[] args) {
//Exceptionin thread "main" java.lang.UnsupportedOperationException
// atjava.util.Collections$UnmodifiableMap.put(Collections.java:1457)
// atcom.gwf.concurrency.example.immutable.ImmutableExample1.main(ImmutableExample1.java:21)
map.put(1,3);
}
}
Guava:Immutablexxx
源码
// ImmutableList
public static ImmutableList of(E e1, Ee2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11) {
returnconstruct(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11);
}
// 超过12个元素,则声明为一个数组
public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, Ee8, E e9, E e10, E e11, E e12, E... others) {
Object[] array = new Object[12 +others.length];
array[0] =e1;
array[1] =e2;
array[2] =e3;
array[3] =e4;
array[4] =e5;
array[5] =e6;
array[6] =e7;
array[7] =e8;
array[8] =e9;
array[9] =e10;
array[10] =e11;
array[11] =e12;
System.arraycopy(others, 0, array, 12, others.length);
returnconstruct(array);
}
private static ImmutableListconstruct(Object... elements) {
for(int i =0; i < elements.length; ++i) {
ObjectArrays.checkElementNotNull(elements[i], i);
}
return newRegularImmutableList(elements);
}
实例
@ThreadSafe
public class ImmutableExample2 {
private final staticList list = ImmutableList.of(1,2,3);
private final staticImmutableSet set = ImmutableSet.copyOf(list);
//奇数位参数为key,偶数位参数为value
private final staticImmutableMap map1 = ImmutableMap.of(1,2,3,5);
private final staticImmutableMap map2 =ImmutableMap.builder()
.put(1,2).put(3,4).build();
public staticvoid main(String[] args) {
//执行都会跑出 UnsupportedOperationException异常
//但是使用ImmutableXXX声明会直接在编译的时候就告诉你这个方法已经被废弃
list.add(5);
set.add(6);
map1.put(1,2);
map2.put(3,4);
}
}
线程封闭
把对象封装到一个线程里,只有这个线程能看到这个对象
实现线程封闭
Ad-hoc 线程封闭:程序控制实现,最糟糕,忽略
堆栈封闭:局部变量,无并发问题
ThreadLocal 线程封闭:特别好的封闭方法
ThreadLocal 实例保存登录用户信息(具体的业务场景,和拦截器的使用就不赘述了,大家可以购买课程详细学习)
public class RequestHolder {
private final staticThreadLocal requestHolder = new ThreadLocal<>();
/**
*添加数据
*在filter里将登录用户信息存入ThreadLocal
*如果不使用ThreadLocal,我们会需要将request一直透传
* @param id
*/
public static voidadd(Long id){
//ThreadLocal内部维护一个map,key为当前线程名,value为当前set的变量
requestHolder.set(id);
}
/**
*获取数据
* @return
*/
public staticLong getId(){
returnrequestHolder.get();
}
/**
*移除变量信息
*如果不移除,那么变量不会释放掉,会造成内存泄漏
*在接口处理完以后进行处理(interceptor)
*/
public static voidremove(){
requestHolder.remove();
}
}
线程不安全的类与写法
1.StringBuilder 线程不安全,StringBuffer线程安全原因:StringBuffer几乎所有的方法都加了synchronized关键字
/**
* 由于StringBuffer 加了 synchronized 所以性能会下降很多
* 所以在堆栈封闭等线程安全的环境下应该首先选用StringBuilder
在方法内部,定义局部变量是封闭的,只有一个线程操作变量,是线程安全的。
*/
@Override
public synchronizedStringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
2.SimpleDateFormat
SimpleDateFormat
在多线程共享使用的时候回抛出转换异常,应该才用堆栈封闭在每次调用方法的时候在方法里创建一个SimpleDateFormat
另一种方式是使用joda-time的DateTimeFormatter(推荐使用)
private staticDateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyyMMdd");
DateTime.parse("20180320",dateTimeFormatter).toDate();
3.ArrayList,HashMap,HashSet等Collections
4.先检查再执行
// 非原子性
if(condition(a)){
handle(a);
}
线程安全-同步容器
1.同一接口,不同实现的线程安全类
第一类
vector的所有方法都是有synchronized关键字保护的。vector 实现了list
stack
继承了vector,并且提供了栈操作(先进后出)
hashtable
也是由synchronized关键字保护
2. Collections.synchronizedXXX (list,set,map)
注意:1.同步容器并不一定线程安全
/**
*并发测试
*同步容器不一定线程安全
* @authorgaowenfeng
*/
@Slf4j
@NotThreadSafe
public class VectorExample2 {
/**请求总数*/
public static intclientTotal = 5000;
/**同时并发执行的线程数*/
public static intthreadTotal = 50;
public staticList list = new Vector<>();
public static voidmain(String[] args) throws InterruptedException {
for (int i= 0; i < 10; i++) {
list.add(i);
}
Threadthread1 = new Thread(() -> {
for (inti = 0; i < 10; i++) {
list.remove(i);
}
});
Threadthread2 = new Thread(() -> {
//thread2想获取i=9的元素的时候,thread1将i=9的元素移除了,导致数组越界
for (inti = 0; i < 10; i++) {
list.get(i);
}
});
thread1.start();
thread2.start();
}
}
注意:2.在foreach或迭代器遍历的过程中不要做删除操作,应该先标记,然后最后再统一删除
public class VectorExample3 {
//java.util.ConcurrentModificationException
//在遍历的同时进行了删除的操作,导致抛出了并发修改的异常
private static voidtest1(Vector v1) { // foreach
for(Integeri : v1) {
if(i.equals(3)) {
v1.remove(i);
}
}
}
//java.util.ConcurrentModificationException
private static voidtest2(Vector v1) { // iterator
Iterator iterator = v1.iterator();
while(iterator.hasNext()) {
Integeri = iterator.next();
if(i.equals(3)) {
v1.remove(i);
}
}
}
// success
private static voidtest3(Vector v1) { // for
for (int i= 0; i < v1.size(); i++) {
if(v1.get(i).equals(3)) {
v1.remove(i);
}
}
}
public static voidmain(String[] args) {
Vector vector = new Vector<>();
vector.add(1);
vector.add(2);
vector.add(3);
test1(vector);
}
}
CopyOnWriteArraySet: 线程安全的,底层实现是CopyOnWriteArrayList,只读操作远大于可读操作,迭代器不支持remove 操作
ConcurrentSkipListSet:支持自然排序,基于map集合,set(),add(), remove() 都是线程安全的 对于批量操作addAll
removeAll() 方法是线程不安全的
作者:Meet相识_bfa5
链接:https://www.jianshu.com/p/7a7f13f091bc
來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友评论