轻量级同步机制volatile三个特性
-
保证可见性,例子: 停止线程的经典例子(boolean running)
-
禁止指令重排,例子:单例模式中signle = new Singleton();
List<String> list = new ArrayList<String>(); list.add("xxx"); list.add("111"); vo.setList(list); //上面的代码可能发生指令重排变为如下 List<String> list = new ArrayList<String>(); vo.setList(list); list.add("xxx"); list.add("111"); //后果 另一个线程拿vo.getList()时虽然不为null,但是是空的。这第一段的话,拿到list不为null,那么肯定非空。当时整个问题引起电视墙下墙失败。
-
不保证原子性,使用AtomicInteger解决
JMM的规范:
jmm内存模型主要解决的就是下面的问题,被问到的时候可以先讲顺序一致性模型,再引申到JMM模型下面的这三个关键点。
- 可见性
- 原子性
- 有序性
CAS(比较、交换)
-
并发原语; UnSafe;自旋
-
CAS缺点:循环时间长;只能保证单个变量原子;ABA
-
ABA解决方案:AtomicStampedReference
手写一个自旋锁
- 定义一个atomicReference
- lock:compareAndSet(null, thread);失败了就自旋再cas
- unlock:compareAndSet(thread, null);这个无需自旋
synchronized与lock的区别
- synchronized是关键字,属于JVM层面,底层依赖monitorenter和monitorexit。
- synchronized不需要手动去释放锁,lock需要手动,配合try finally语句块来使用
- synchronzied不可而中断,除非线程本身抛异常了。lock可中断:1.设置超时时间trylock(long timeout, TimeUnit unit)2.lockInterruptibly()代替lock()
- synchronized只能是非公平锁,lock可以是公平或非公平
- synchronzied的等待唤醒只能随机唤醒或全部唤醒,而lock的等待唤醒利用多个condition可以精确唤醒。
线程池的优势
- 消除频繁创建和销毁线程的系统资源开销
- 面对过量任务的提交能够平缓的劣化
- 提高线程的可管理性。
- 如果一个任务一个线程,那可能有几千个线程,操作系统频繁进行上下文切换,无故增加系统的负载。
线程池合理的线程数
-
CPU密集型:CPU核心数+1
-
IO密集型:
公式:Nthread = Ncpu * Ucpu * (1+ W/C),各字段含义:
Nthreads:线程数量
Ncpu:CPU的数量,Runtime.getRuntime().availableProcessors()
Ucpu:CPU使用率,范围在[0,1]
W/C:等待时间与计算时间的比率
线程池大小还和资源有关,比如某个线程池全部需要数据库连接,而数据库连接池内连接的数量有限,那么线程池大小不应该比数据库连接池的大。
死锁的是什么、定位方法、避免
- 死锁是什么,太简单,不写了
- 用jps -l查出java进程,jstack pid 获得堆栈,日志会列出死锁情况
- 避免手段:1.加锁顺序2.加锁时限3.死锁检测