java
使用线程的四种方式
1、重写Thread类的run()方法,调用start()方法启动线程。
2、实现Runable接口的run()方法,作为参数传入new Thread(),再调用start()方法启动线程。
3、实现Callable接口的call()方法,作为参数传入new FutureTask(),
再作为参数传入new Thread(),调用start()方法启动线程,futuretask对象调用get()方法可获得返回值。
4、Executors创建线程池;
调用submit()提交实现了Runable接口Callable接口的对象,通过返回的future对象调用get()方法可获得返回值;
调用execute()提交实现了Runable接口的对象执行。
继承、实现(封装、多态)
1、抽象类继承实体类和实体类继承抽象类一样;但子类构造器必须能访问父类的同类型构造器;
所有类包括抽象类都直接或间接继承Object这个实体类。
2、接口没有构造器,因此不能继承类。
3、类只能单继承类多实现接口,接口可以多继承接口。
4、this指向自己,super指向父类。
5、final类不能被继承,保证该类访问安全,例如String类;
final方法在子类不能被重写,但可以被继承;
final修饰的变量不能被修改。
6、abstract只能修饰类和方法;abstract修饰的方法不能有{}。
7、static修饰的方法能被继承,子类可以有相同方法,但不构成重写关系,
父类引用指向子类时的多态形式和成员变量类似;
static只能修饰成员变量,具体方法,代码块,为所有实例对象共享。
8、静态代码块只在类被虚拟机加载的时候执行一次,普通代码块在new对象的时候执行一次。
9、重写出现在继承关系中,方法名参数列表一致,返回类型,异常只能是自己或子类,访问范围不能缩小;
重载出现在同个类中,方法名一致,参数列表不一致,返回类型可不一样;
List
1、ArrayList 数组结构,默认初始大小10,扩容1.5倍,线程非安全
2、Vector 数组结构,默认初始大小10,默认扩容2倍, 线程安全,该类的方法都加synchronized关键字实现
3、LinkedList 私有内部类Node的链表结构
Set
1、HashSet 唯一可以有1个NULL,线程非安全,底层是一个hashMap的哈希表,KEY作为集合元素
2、TreeSet 唯一不能有NULL,线程非安全,底层是一个treeMap的红黑树,KEY作为集合元素
Map
1、HashMap 一个链表结构的内部类Node组成的数组存储,初始长度16,超过容量的0.75调用resize()扩容一倍;
根据Node的key的hash值计算索引查询数组中的Node节点;
若不存在,则新建一个Node节点,塞入该位置;
若存在,则判断该节点的key是否和传入的key相同,相同则修改value值;
不相同,表示出现hash碰撞,遍历寻找下一个Node节点,直到key相同修改value;
或者下一个Node节点为NULL,新创建该节点。
当数组大于64,Node节点的链表长度>=8转为红黑树,提高索引效率,非线程安全。
2、TreeMap 非线程安全,不允许NULL,底层结构是一个红黑树
3、HashTable 结构和hashmap类似,线程安全,该类的方法都加synchronized关键字实现,不允许NULL
4、LinkedHashMap 非线程安全,在hashmap基础上Node节点增加befor,after指向前后节点
5、ConcurrentHashMap 线程安全,在hashmap的基础上对数组中的Node节点加锁
JAVA中的数据结构
1、栈,先进先出
2、队列,队首出pop、队尾入push
3、链表
4、散列表
5、排序二叉树
6、红黑树
7、B-TREE、B+TREE
8、位图
加密算法
1、对称加密
2、非对称加密
3、MD5
JVM
1、java编译器把源代码编译成字节码
2、类加载器加载类的字节码文件,交由JVM执行引擎执行
3、程序执行期间存储的数据区域称为运行时数据区,即JVM内存
4、程序计数器,线程私有,记录需要执行指令的地址,若执行native方法则值为null,不会内存溢出
5、虚拟机栈,线程私有,每个栈帧对应一个被调用的方法,
每个栈帧包括局部变量表,操作数栈,指向运行时常量池的引用,方法返回地址
6、本地方法栈,线程私有,执行native方法
7、堆,线程共享,存储对象和数组
8、方法区,线程共享,存储类信息,静态变量,常量,编译后的代码;
JAVA8以后类信息放入元空间存储在本地内存,静态变量和常量放入堆中
9、新生代占堆的1/3、Eden占新生代8/10,存放新创建的对象,对象过大直接进入老年代,内存不足触发Minor GC;
From Servivor占新生代1/10,存放上次GC幸存者作为本次被扫描者;
To Servivor占新生代1/10,保留本次GC幸存者,一次Minor Gc后From和To 互换
10、老年代占堆2/3,生命周期长,空间不足触发Major GC
11、标记清除法,扫描两次容易产生内存碎片,适用存活对象较多的老年代;
复制算法,标记存活对象复制移动需要一块空内存空间,清除之前的内存空间,适用存活对象较少的于新生代;
标记压缩法,改进的标记清除法,把存活的对象压缩到内存一端,清除外边界空间,使用于老年代;
分代收集算法,新生代用复制算法,老年代用标记压缩法。
分区收集算法,把堆分为连续的小空间,每个小空间独立使用独立回收,减少GC产生的停顿。
12、强引用,不会被回收,可能导致内存泄漏;
软引用,适用于高速缓存,在堆空间临近阈值被回收,sf.get()为null,SoftReference sf = new SoftReference<>(obj);
弱引用,只要垃圾回收触发就被回收,sf.get()为null,WeakReference sf = new WeakReference(obj);
虚引用,必须和引用队列一起使用,用来跟踪垃圾回收
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference sf = new PhantomReference<>(obj,referenceQueue);
13、每一种垃圾收集算法都有一种垃圾收集器,又分为单线程和多线程收集器
14、加载,验证,准备,解析(符号引用替换为直接引用),初始化
15、启动类加载器,扩展类加载器、应用程序类加载器;
(双亲委派,依次委托父类加载,父类加载不了再由子类加载,确保加载的是同一个Object对象)
16、new一个对象,JVM依据方法区加载的类信息,栈中分配引用指向堆中创建的实例对象
线程池
1、newFixedThreadPool定长线程池
2、newCachedThreadPool可缓冲线程池
3、newSingleThreadExecutor单任务线程池
4、ScheduledThreadPool周期线程池
5、线程池创建后没有线程,小于核心线程,新任务来会创建线程,直到达到核心线程;
超过核心线程会放入阻塞队列,超过阻塞队列,会创建非核心线程直到达到最大线程;
超过阻塞队列和最大线程才用拒绝策略,大于核心线程的空闲线程超过存活时间会回收。
拒绝策略:1主线程执行,2丢弃缓存队列最老的任务,3拒绝执行,4抛出异常
6、线程复用(重写run方法)、控制最大并发数、管理线程
7、线程池组成:线程池管理器、工作线程、任务队列、任务接口
线程
1、线程的五种状态:new新建,start()就绪,run()拿到CPU运行,阻塞wait()等待队列、lock()锁池、sleep()join()阻塞,
死亡run()call()执行完毕、异常、stop()容易导致死锁
2、sleep()让出CPU不释对象放锁,wait()释放对象锁进入等待对象锁池,notify()准备获取对象锁
3、start()启动线程,run()线程执行的方法体
4、守护线程setDaemon(true),如垃圾回收线程,守护线程存活依赖非守护线程
5、线程等待(wait)、线程睡眠(sleep)、线程让步(yield)、线程中断(interrupt)、等待线程终止join()、
线程唤醒(notify)、线程存活isAlive()、当前线程currentThread()、活跃线程数activeCount()、线程名称setName()、
线程优先级setPriority()、守护线程isDaemon()
6、时间片轮转实现线程上下文切换,线程并行于父进程中,(时间片用完、IO阻塞、没抢到锁被调度器挂起)
阻塞队列
1. ArrayBlockingQueue :由数组结构组成的有界阻塞队列。(公平、非公平) new ArrayBlockingQueue(1000,true);
2. LinkedBlockingQueue :由链表结构组成的有界阻塞队列。(两个独立锁提高并发)
3. PriorityBlockingQueue :支持优先级排序的无界阻塞队列。(compareTo 排序实现优先)
4. DelayQueue:使用优先级队列实现的无界阻塞队列。(缓存失效、定时任务 )
5. SynchronousQueue:不存储元素的阻塞队列。(不存储数据、可用于传递数据)
6. LinkedTransferQueue:由链表结构组成的无界阻塞队列。可以直接传递给消费者
7. LinkedBlockingDeque:由链表结构组成的双向阻塞队列。可以从队列的两端插入和移出元素
8、poll(time)取走元素超时返回Null,take()等待取到,add(E paramE)放入满抛出异常, put(E paramE)放入满等待
锁
1、同步锁、独占锁、互斥锁,同时只允许一个线程访问数据
Synchronized作用普通方法锁住对象,作用静态方法锁住类,非公平锁
ReentrantLock lock(): unlock():=new ReentrantLock(true);//公平锁new ReentrantLock(false);//非公平锁
Semaphore 可选择是否公平锁
2、死锁,线程被阻塞,锁无法释放
3、乐观锁,通过比对更新,CAS
4、悲观锁,每次写都会上锁,Synchronized
5、自旋锁,短时间循环尝试获取锁,超时进入阻塞等待
6、适应性自旋锁,
由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定,由CPU数量和正在自旋的线程决定,节电模式停止自旋
7、公平锁,先申请获取锁的线程先获得锁
8、非公平锁,就近随机原则分配锁,性能比公平锁高 5~10 倍
9、偏向级锁,只有一个线程执行同步代码块,只执行一次CAS操作
10、轻量级锁,交替执行同步代码块,不替代重量级锁,锁住得是对象直接膨胀为重量级锁
11、重量级锁,Synchronized依赖操作系统实现的重量级锁效率低
12、锁升级,无锁、偏向锁、轻量级锁、重量级锁,不会出现锁降级
13、可重入锁,同个线程可重复获取锁
14、读写锁,读上读锁(只能有一个写),写上写锁(只能有一个写,且不能同时读)
15、共享锁(读写锁,乐观锁)
16、分段锁,减小锁粒度
17、锁分离,读写分离
18、锁粗化,多次获取锁的请求合并成一个,避免多次申请释放带来的锁开销
19、锁消除,编译时发现不可能共享的对象,消除锁操作
CyclicBarrier、CountDownLatch、Semaphore
1、CountDownLatch(线程计数器 ),
CountDownLatch latch = new CountDownLatch(2);latch.countDown();atch.await();
1个线程等待其它线程
2、CyclicBarrier(回环栅栏-等待至 barrier 状态再全部同时执行)
CyclicBarrier barrier = new CyclicBarrier(N);
barrier .await();挂起当前线程,N个线程都挂起,进入barrier 状态,全部同时执行,可重用
全部线程互相等待,再同时执行
3、Semaphore(信号量-控制同时访问的线程个数)
Semaphore semaphore = new Semaphore(5);5个许可类似锁
semaphore.acquire();获取一个许可;semaphore.release();释放一个许可
volatile、sychronized、ReentrantLock、ThreadLocal
1、synchronized 可重入锁 JVM级别 关键字
2、ReentrantLock 可重入锁 API级别 lock接口
3、ThreadLocal 线程本地变量,ThreadLocalMap是Thread的一个属性,
ThreadLocalMap使用ThreadLocal 的弱引用作为key,若没有外部强引用则会回收,
此时key为nul,Value存在强引用,可能造成内存泄漏
每次使用ThreadLocal后要调用remove()清楚数据,避免内存泄漏
4、JVM的内存模型JMM解决可见性、有序性,锁解决原子性
5、volatile变量可见性,禁止指令重排序,只有单次读/写操作能保证原子性,比sychronized更轻量级
线程调度
1、先来先服务优先调度算法、短作业优先调度算法
2、非抢占式优先调度算法、抢占式优先调度算法、高响应优先调度算法
3、时间片轮转法、多级反馈调度算法
线程调度算法(抢占式调度算法、协同式调度算法)
CAS、ABA、 AQS
1、CAS比较并交换,更新时被更新值等于旧值才更新,但是会出现ABA,通过版本号递增避免ABA;
原子类使用CAS实现, AtomicReference<T>让对象的操作原子性通过volatile和CAS实现
AQS抽象队列同步器,共享资源(Semaphore/CountDownLatch),独占资源(ReentrantLock)
错误和异常
1、运行时异常、编译时异常
2、throws 抛弃异常在方法上,throw捕获抛出在方法内
反射
1、编译时类型、运行时类型
2、获取CLASS的三种方法
对象的 getClass()方法;Person p=new Person();Class clazz=p.getClass()
类的 class 属性;Class clazz=Person.class;
Class 类中的 forName()静态方法(最安全/性能最好);Class clazz=Class.forName("类的全路径"); (最常用)
3、clazz.getDeclaredMethods(); 获取方法
clazz.getDeclaredFields(); 获取属性
clazz.getDeclaredConstructors(); 获取构造器
4、创建对象的三种方式
new;
反射类对象.newInstance(),此方法必须有默认构造器;
反射类构造器.newInstance()。
注解
1、Annatation(注解)是一个接口,通过反射获取程序中对象的注解元数据信息
2、@Target 修饰的对象范围;@Retention 定义 被保留的时间长短;@Documented 描述-javadoc;@Inherited 阐述了某个被标注的类型是被继承的
3、注解处理器,反射是否有.isAnnotationPresent(注解);反射获取getAnnotation(注解),注解.属性或者方法
内部类
1、静态内部类、成员内部类、局部内部类(定义在方法中的类)、
匿名内部类(要继承一个父类或者实现一个接口、直接使用new 来生成一个对象的引用)
泛型
1、编译时类型擦除
序列化
1、持久化和远程传输需要序列化,不会保存静态成员
2、Transient修饰的变量不会被序列化
3、虚拟机反序列化需要ID一致
4、修改序列化方法,writeObject(ObjectOutputStream out), readObject(ObjectInputStream in)
复制
1、直接赋值复制,引用
2、浅复制 implements Cloneable,super.clone();(复制引用但不复制引用的对象)
3、深复制 implements Cloneable,o = (Student) super.clone();(复制对象和其引用对象)
NIO
1、流.getchanel方法获取通道
内存溢出
1、启动参数内存设置过小、代码运行后处于死循环、
一次性加载数据量过大、集合对象的引用,没有及时清理,jvm不能回收空间
排查堆内存溢出
1、jvisualvm命令窗口、Visual GC插件、dump堆信息、MAT分析工具
2、用jmap查看存活的对象情况(jmap -histo:live [pid])
查看堆栈信息 jmap -heap 16190
执行jmap -dump:format=b,file=heap.bin 16190,生成堆栈文件,通过MAT进行分析
3、Btrace脚本 下载安装
内存和CPU问题
top -p ${pid}
查看该进程关联线程情况
top -H -p ${pid}
查看系统的堆情况
jmap -heap ${pid}
查询那些实例占用内存
jmap -histo ${pid}
具体到包名
jmap -histo ${pid} | grep ${package}
查询GC情况
jstat -gcutil ${pid} 1000 10
建议在JVM启动时开启 -XX:+HeapDumpOnOutOfMemoryError,该命令可以让应用在发生OOM时,
自动生成HeapDump的转储文件,方便问题排查
DK自带的工具,jps jmap jstat
系统重构
jvm调优
1、-Xms和-Xmx的值设置成相等,堆大小默认为-Xms指定的大小,默认空闲堆内存小于40%时,
JVM会扩大堆到-Xmx指定的大小;空闲堆内存大于70%时,JVM会减小堆到-Xms指定的大小。
如果在Full GC后满足不了内存需求会动态调整,这个阶段比较耗费资源。
2、新生代尽量设置大一些,让对象在新生代多存活一段时间,
每次Minor GC 都要尽可能多的收集垃圾对象,防止或延迟对象进入老年代的机会,
以减少应用程序发生Full GC的频率。
3、避免创建过大的对象及数组:过大的对象或数组在新生代没有足够空间容纳时会直接进入老年代,
如果是短命的大对象,会提前出发Full GC。
4、避免同时加载大量数据,如一次从数据库中取出大量数据,或者一次从Excel中读取大量记录,
可以分批读取,用完尽快清空引用。
5、可以在合适的场景(如实现缓存)采用软引用、弱引用,
比如用软引用来为ObjectA分配实例:SoftReference objectA=new SoftReference();
在发生内存溢出前,会将objectA列入回收范围进行二次回收,如果这次回收还没有足够内存,
才会抛出内存溢出的异常。
避免产生死循环,产生死循环后,循环体内可能重复产生大量实例,导致内存空间被迅速占满。
6、尽量避免长时间等待外部资源(数据库、网络、设备资源等)的情况,缩小对象的生命周期,
避免进入老年代,如果不能及时返回结果可以适当采用异步处理的方式等。
7、
-Xms 初始堆大小,默认物理内存的1/64 -Xms512M
-Xmx 最大堆大小,默认物理内存的1/4 -Xms2G
-Xmn 新生代内存大小,官方推荐为整个堆的3/8 -Xmn512M
-Xss 线程堆栈大小,jdk1.5及之后默认1M,之前默认256k -Xss512k
-XX:NewRatio=n 设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 -XX:NewRatio=3
-XX:SurvivorRatio=n 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8 -XX:SurvivorRatio=8
-XX:PermSize=n 永久代初始值,默认为物理内存的1/64 -XX:PermSize=128M
-XX:MaxPermSize=n 永久代最大值,默认为物理内存的1/4 -XX:MaxPermSize=256M
-verbose:class 在控制台打印类加载信息
-verbose:gc 在控制台打印垃圾回收日志
-XX:+PrintGC 打印GC日志,内容简单
-XX:+PrintGCDetails 打印GC日志,内容详细
-XX:+PrintGCDateStamps 在GC日志中添加时间戳
-Xloggc:filename 指定gc日志路径 -Xloggc:/data/jvm/gc.log
-XX:+UseSerialGC 年轻代设置串行收集器Serial
-XX:+UseParallelGC 年轻代设置并行收集器Parallel Scavenge
-XX:ParallelGCThreads=n 设置Parallel Scavenge收集时使用的CPU数。并行收集线程数。 -XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=n 设置Parallel Scavenge回收的最大时间(毫秒) -XX:MaxGCPauseMillis=100
-XX:GCTimeRatio=n 设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) -XX:GCTimeRatio=19
-XX:+UseParallelOldGC 设置老年代为并行收集器ParallelOld收集器
-XX:+UseConcMarkSweepGC 设置老年代并发收集器CMS
-XX:+CMSIncrementalMode 设置CMS收集器为增量模式,适用于单CPU情况。