面试二

2019-05-19  本文已影响0人  我是嘻哈大哥

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活跃用户
上一篇 下一篇

猜你喜欢

热点阅读