1.JSR-303 @Valid验证引发BindException异常还是MethodArgumentNotValidException异常
1.当使用@Valid不带@RequestBody request参数时,当对象验证失败时,验证将引发BindException而不是MethodArgumentNotValidException。
2.当你@valid 带@RequestBody 时候是 MethodArgumentNotValidException。
小结:所以在进行全局异常处理的时候,你注意你应该用什么异常,不确定的时候,一起加入全局异常。
@ExceptionHandler(value = BindException.class)
@ExceptionHandler(value = MethodArgumentNotValidException.class)
2.自定义校验
第一步:
第二步:
第三步:
引入:
https://www.cnblogs.com/sanboshi/p/9378666.html
3.多线程 arrayList() 安全问题
现在有个简单的需求,就是多个线程,对同一个list 集合进行add操作。代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 解决安全问题的替换方法
* 1.vector
* 2.synchronizedList(new ArrayList)
* 3.CopyOnWriteArrayList
*/
public class NoSafe {
public static void main(String[] args) throws Exception {
List<String> list = new ArrayList<>();
ExecutorService executorService = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
executorService.execute(() -> {
list.add(UUID.randomUUID().toString().substring(0, 8));
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list);
latch.countDown();
});
}
latch.await();
System.out.println(list);
executorService.shutdown();
}
}
打印结果
[77cda994, 530023fa]
[77cda994, 530023fa]
[77cda994, 530023fa]
[77cda994, 530023fa]
数据不对。
有时候还会有ConcurrentModificationException 阻塞异常。
解决方案,将arrayList 替换为。
1.new vector()
2.new synchronizedList(new ArrayList)
3.new CopyOnWriteArrayList()
4.synchronized 的操作范围
- 锁同一个对象时候,如果这个对象多个方法有synchronized 时候都会被影响。这个对象的普通方法不会受影响。
- Synchronized如果锁的是static 方法,则这个对象对应的class ,其他实例其他static synchronized方法都会被影响。Synchronized(synchronized 与static synchronized 相当于两帮派,各自管各自,相互之间就无约束了) 和普通方法不会被影响。
5.CopyOnWriteArrayList
写入时复制(CopyOnWrite)思想
写入时复制(CopyOnWrite,简称COW)思想是计算机程序设计领域中的一种优化策略。其核心思想是,如果有多个调用者(Callers)同时要求相同的资源(如内存或者是磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者视图修改资源内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。
说白了,就是读写分离,等写完了(写的过程加锁,读不受影响,读还是读的旧的对象),将新对象引用,旧对象删除。
CopyOnWriteArrayList的实现原理
在使用CopyOnWriteArrayList之前,我们先阅读其源码了解下它是如何实现的。以下代码是向CopyOnWriteArrayList中add方法的实现(向CopyOnWriteArrayList里添加元素),可以发现在添加的时候是需要加锁的,否则多线程写的时候会Copy出N个副本出来。
写操作
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
读的时候不需要加锁,如果读的时候有多个线程正在向CopyOnWriteArrayList添加数据,读还是会读到旧的数据,因为写的时候不会锁住旧的CopyOnWriteArrayList。实现读写分离,直到写完了,会把旧对象直接= 给 新对象。这样读很快,但一次写会Arrays.copyOf 新的数组对象,消耗内存多,所以建议读操作多的可以使用这个集合保证线程安全。
读操作
public E get(int index) {
return get(getArray(), index);
}
JDK中并没有提供CopyOnWriteMap,我们可以参考CopyOnWriteArrayList来实现一个,基本代码如下:
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {
private volatile Map<K, V> internalMap;
public CopyOnWriteMap() {
internalMap = new HashMap<K, V>();
}
public V put(K key, V value) {
synchronized (this) {
Map<K, V> newMap = new HashMap<K, V>(internalMap);
V val = newMap.put(key, value);
internalMap = newMap;
return val;
}
}
public V get(Object key) {
return internalMap.get(key);
}
public void putAll(Map<? extends K, ? extends V> newData) {
synchronized (this) {
Map<K, V> newMap = new HashMap<K, V>(internalMap);
newMap.putAll(newData);
internalMap = newMap;
}
}
}
引用:https://www.cnblogs.com/myseries/p/10877420.html