1.volatile
轻量级的同步机制
特性:
(1)保证可见性 缓存-主存
(2)不保证原子性 atomic
(3)禁止指令重排序 编译器优化程序
使用场景:单例模式
2.CAS
CompareAndSet
底层原理
Unsafe类
3.集合不安全
3.1 ArrayList
故障:多线程下使用不安全 java.util.ConcurrentModificationException
导致原因:并发争抢修改导致,参考我们的花名册情况,一个人正在写入,另一个同学过来抢,并发写入异常
解决方案:
3.1.1 List<String> list=new Vector<>();
3.1.2 List<String> list=Collections.synchonizedList(new ArrayList<>());
3.1.3 List<String> list=new CopyOnWriteArrayList<>();//写时复制 读写分离
3.2 HashSet(底层是HashMap)
故障:多线程下使用不安全 java.util.ConcurrentModificationException
解决方案:
3.2.1 Set<String> set=Collections.synchonizedSet(new HashSet<>());
3.2.2 Set<String> set=new CopyOnWriteArraySet<>(); //底层还是CopyOnWriteArrayList
HashSet.add(E);
->HashMap.put(E,new Object());//添加的key
3.3 HashMap
故障:多线程下使用不安全 java.util.ConcurrentModificationException
解决方案:
3.2.1 Map<String> map=Collections.synchonizedMap(new HashMap<>());
3.2.2 Map<String> map=new ConcurrentHashMap<>();
4.公平锁/非公平锁、可重入锁、独占锁/共享锁、自旋锁
公平锁:只多个线程按照申请的顺序来获取锁,类似排队打饭,先来后到
非公平锁:抢占,优点吞吐量大。ReentrantLock:可以使用构造函数中的boolean类型来获得公平锁或公平锁,默认为非公平锁。对于Synchonized,也是一种非公平锁
可重入锁(又名递归锁):线程可以进入任何一个它已经拥有锁所同步的代码块。ReentrantLock和Synchonized就是可重入锁,最大作用就是避免死锁
自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
public class SpinLock {
//原子引用线程
private AtomicReference<Thread> cas = new AtomicReference<Thread>();
public void lock() {
Thread current = Thread.currentThread();
// 利用CAS
while (!cas.compareAndSet(null, current)) {
// DO nothing
}
}
public void unlock() {
Thread current = Thread.currentThread();
cas.compareAndSet(current, null);
}
}
5、ReentrantReadWriteLock
读写锁
6.CountDownLatch
减计数,到0时释放锁——秦灭六国,然后一统天下
7.CycllicBarrier
增计数,到指定值后释放锁——七龙珠
8.Semaphore
多个线程抢占多个资源,可替代synchorized和Lock
9.阻塞队列
9.1阻塞队列有没有好的一面
好处是不需要关心什么时候需要阻塞线程,什么时候需要唤醒
9.2 核心方法
>Collection
>>List
>>Queue
>>>BlockingQueue
>>>>ArrayBlockingQueue 数组组成的有界阻塞队列
>>>>LinkedBlockingQueue 链表组成的有界阻塞队列
>>>>PriporityBlockingQueue
>>>>DealyQueue
>>>>SynchronousQueue 同步队列,单个元素的队列
>>>>LinkedTransferQueue 链表组成的无界阻塞队列
>>>>LinkedBlockingDeque 链表组成的双向阻塞队列
BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);
blockingQueue.add("a"); //抛异常
blockingQueue.element();
blockingQueue.remove();
blockingQueue.offer("a");//返回true或false
blockingQueue.peak();
blockingQueue.poll();
blockingQueue.put("a");//阻塞
blockingQueue.take();
blockingQueue.offer("a",2L,TimeUnit.SECOND);//过时不候
//生产者消费者案例
线程 操作 资源类
判断 干活 通知
防止虚假唤醒
//传统版
class ShareData{
private int number=0;
private Lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void increment() throw Execption{
Lock.lock();
try{
//判断
while(number!=0){
//等待,不能生产
condition.await();
}
//干活
number++;
//通知唤醒
condition.singalAll();
}
catch(Execption e){
e.printStackTrace();
}
finally{
Lock.unlock();
}
}
public void decrement() throw Execption{
Lock.lock();
try{
//判断
while(number==0){
//等待,不能生产
condition.await();
}
//干活
number--;
//通知唤醒
condition.singalAll();
}
catch(Execption e){
e.printStackTrace();
}
finally{
Lock.unlock();
}
}
}
synchonized和lock区别
(1)synchonized 关键字 底层是monitor(monitorenter、monitorexit-正常和异常退出)实现,lock是具体的类
(2)使用方法,synchonized自动释放,lock是手动释放
(3)等待是否可中断。synchonized不可中断,lock可以中断
(4)加锁是否公平。synchonized非公平锁,lock可以设置,默认是非公平
(5)锁绑定多个condition
class ShareData{
private int number=1;
private Lock lock=new ReentrantLock();
private Condition c1=lock.newCondition();
private Condition c2=lock.newCondition();
private Condition c3=lock.newCondition();
public void print5(){
Lock.lock();
try{
while(number!=1){
c1.await();
}
//干活
//通知唤醒c2
number=2;
c2.signal();
}
catch(Execption e){
e.printStackTrace();
}
finally{
Lock.unlock();
}
}
}
10 线程池
10.1 七大参数
ThreadPoolExecutor的7大参数
corePoolSize 核心线程数 银行今日当值2个窗口
maximumPoolSize 最大线程数 银行最大5个窗口
keepAliveTime 多余空闲线程存活时间 扩容后,规定时间内没有多余的请求后,销毁扩容的线程,直到只剩下核心线程
unit 单位
workQueue 阻塞队列 银行候客区
threadFactory 线程工厂 用于创建线程的工厂,一般默认即可
handler 拒绝策略 有4种
10.2 线程池底层工作原理
四步工作流程:
1.少于核心数,直接在核心数执行
2.大于核心数,多余的进入阻塞队列
3.大于阻塞队列,开启扩容到最大线程数
4.还再加大,开启拒绝策略
5.扩容区空闲超出空闲时间,释放扩容区。
10.3 拒绝策略RejectedExecutionHandler
AbortPolicy 直接抛出异常
CallerPolicy 回退任务到调用者
DiscardOldestPolicy 丢弃等待时间最长的
DiscardPolicy 直接丢弃
10.4 工作中用到的线程池
常用的三个(newSingle newFixed newCache)一个都不用? 有可能导致OOM
10.5 自定义线程池编写
ExectorService threadPool=new ThreadPoolExecutor(2,
5,
1L TimeUnit.SECOND,
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory,
new ThreadPoolExecutor.AbortPolicy);
1) AbortPolicy 超出8个后直接抛出异常
2) CallerPolicy 回退任务到调用者,如main线程启动的线程则回退到main线程
3) DiscardOldestPolicy 丢弃
4) DiscardPolicy 丢弃
合理配置线程池线程数:
1)获取运行服务器的cpu核心数;
2)CPU密集型:1+cpu核心数
2)IO密集型:(1)cpu核心数*2 (2)cpu核心数/1-阻塞系数 阻塞系数0.8-0.9
11.死锁编码及定位分析
两个或两个以上的线程在执行过程中,因争抢资源而出现互相等待
class HoldLockThread implements Runnable{
private String lockA;
private String lockB;
public HoldLockThread(String lockA,String lockB){
This.lockA=lockA;
This.lockB=lockB;
}
public void run(){
synchonized(lockA){
sout("自己持有A锁,尝试持有B锁");
synchonized(lockB){
sout("自己持有B锁,尝试持有A锁");
}
}
}
}
new Thread(new HoldLockThread(lockA,lockB)).start();
new Thread(new HoldLockThread(lockB,lockA)).start();
如何证明出现死锁?
jps -l 获得进程号
jstack 9636 Found 1 deadLock
12.JVM
12.1
垃圾回收的区域:方法区 堆
回收算法:
引用计数
复制回收
标记清除
标记整理
GCRoot
12.2 JVM参数
jps -l 查看Java后台进程
jinfo -flag 具体参数 进程号
jinfo -flags 进程号
标配参数
X参数
XX参数
1)布尔类型
公式:+(开启) -(关闭)
-XX:+PrintGCDetails
2)KV设值型
-XX:MetaspaceSize=1024m
-XX:MaxTenuringThreshold=15
坑题:
-Xms1024m 等价于 -XX:InitialHeapSize
-Xmx1024m 等价于 -XX:MaxHeapSize
查看参数盘点家底
java -XX:+PrintFlagsInitial = :=
Java中的几种引用方式
Java中有几种不同的引用方式,它们分别是:强引用、软引用、弱引用和虚引用。下面,我们首先详细地了解下这几种引用方式的意义。
强引用
在此之前我们介绍的内容中所使用的引用 都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。
当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
软引用(SoftReference )
SoftReference 类的一个典型用途就是用于内存敏感的高速缓存。 SoftReference 的原理是:在保持对对象的引用时保证在 JVM 报告内存不足情况之前将清除所有的软引用。
关键之处在于,垃圾收集器在运行时可能会(也可能不会)释放软可及对象。对象是否被释放取决于垃圾收集器的算法 以及垃圾收集器运行时可用的内存数量。
弱引用(WeakReference )
WeakReference 类的一个典型用途就是规范化映射( canonicalized mapping )。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。
关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放 WeakReference 引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。
虚引用(PhantomReference )
PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行 pre-mortem 清除操作。 PhantomReference 必须与 ReferenceQueue 类一起使用。
需要 ReferenceQueue 是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时, PhantomReference 对象就被放在它的 ReferenceQueue 上。
将 PhantomReference 对象放在 ReferenceQueue 上也就是一个通知,表明 PhantomReference 对象引用的对象已经结束,可供收集了。
这使您能够刚好在对象占用的内存被回收之前采取行动。 Reference与 ReferenceQueue 的配合使用
如何选择垃圾收集器?
组合选择
单cpu或小内存,单机程序
-XX+UseSericalGC
多cpu,需要大量吞吐量,如后台计算型应用
-XX+UseParallelGC或者
-XX+UseParallelOldGC
多cpu,追求地停顿时间,需要快速响应互联网应用
-XX:+UseConcMarkSweepGC
-XX:+ParNewGC
G1垃圾收集器
服务端垃圾收集器,应用在多处理器和大量内存环境,在实现高吞吐的同时,还具有以下点:
与CMS一样,可以高并发执行
预测GC停顿时间
不希望牺牲更大的吞吐性能
不需要更大的java heap
主要改变是Eden,survivor和tenured等区域不是连续的,而是变成了一个大小一样的region,每个region大小不等,从1M-32M,每一个region有可能属于Eden,survivor和tenured
底层原理:
region区域化垃圾收集器 默认拆分2048个分区
32M*2048=64G最大内存
//cpu
top uptime
vmstat -n 2 3
mpstat -P ALL 2
pidstat -u 1 -p 进程号
//内存
free -m
pidstat -p 进程号 -r 间隔时间
//磁盘IO
df -h
iostat -xdk 2 3
pidstat -d 采样间隔 -p 进程号
//网络IO
ifstat 1
生产环境cpu过高怎么办?
1.先用top找cpu占比最高的;
2.定位后台那个程序影响到的;
3.定位到具体线程和代码; ps -mp 进程 -o THREAD,tid,time
4.将需要的线程id转换成16进制格式
5.jstack 进程id|grep pid(16进制ID或者小写英文格式) -A60
github骚操作
in:name //名字中包含
in:readme //readme中包含
in:despcription //描述包含
in:name,readme //组合使用
springboot stars:>=5000 //点赞数超过5000的spring boot项目
springcloud forks:>500 //fork数大于500的spring cloud项目
springboot stars:100..200 forks:100..200 //组合区间查询
awesome redis //awesome 一般收集、学习相关技术的搜素方法
https://github.com/codingXiaxw/seckill/blob/157ebd15ffce3434d8039670a95a5fe000c0fb97/src/main/java/cn/codingxiaxw/dao/SeckillDao.java#L13 //#L13 高亮显示13行
https://github.com/codingXiaxw/seckill/blob/157ebd15ffce3434d8039670a95a5fe000c0fb97/src/main/java/cn/codingxiaxw/dao/SeckillDao.java#L13-L23 //#L13-L23 高亮13-23行
t 项目内搜索
location:beijing language:java //查找北京java活跃用户
网友评论