1.java的创建线程
-
继承Thread类
-
实现runnable接口
public static void main (String[]aegs){
Thread thread =new Thread(()->System,out.println("hello wlw"));
thread.start();
}
3.实现callable接口
// 创建一个FutureTask对象,并传入一个wlwThread实例作为任务
FutureTask<String> futureTask = new FutureTask<>(new wlwThread());
// 创建一个新的线程,并将FutureTask对象作为参数传递给线程
Thread thread = new Thread(futureTask);
// 启动线程,任务将在后台执行
thread.start();
// 获取任务的结果,会阻塞主线程直到任务执行完毕并返回结果
String result = futureTask.get();
FutureTask实现了RunnableFuture接口,而RunnableFuture实现了Future和Runnable接口,并继承了Future的get()方法
类是单继承,接口可以是多继承
4.线程池
ExecutorService executorService =Executors.newFixedThreadPool(10);
executorService.execute(new wlwThread());
Executors工具类,用于创建一个固定大小的线程池
通过execute()方法,可以将任务交给线程池执行
2.不建议使用Excutors来创建线程池
因为会用到LinkedBlockingQueue,无界队列,会造成OOM,而且不能自定义线程的线程配置,也不能定义名字,不利于排查问题。建议使用ThreadPoolExecutor来定义线程池。
int corePoolSize = 10; // 核心线程数
int maxPoolSize = 20; // 最大线程数
long keepAliveTime = 60; // 空闲线程的存活时间(秒)
int queueCapacity = 100; // 队列容量
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity)
);
3.Sychronized
-
java中的一个关键词
实现方法或代码块
public synchronized void increment() {
count++;
}
- 4.自动加锁与释放锁
其他线程在此期间无法进入相同对象的其他synchronized方法或代码块,它们会被阻塞等待锁的释放,保证了线程安全性。
-
JVM层面的锁(对象锁,监视器锁)(具体实现)
-
非公平锁
即使有其他线程在等待获取锁,jvm也会直接将锁分配给请求线程,以公平的方式分配锁
-
锁的是对象,锁信息保存在对象头中(标记字段,锁记录)
-
底层有锁升级过程(偏向锁,轻量级锁,重量级锁)
5.Tomcat用自定义类加载器
因为tomcat可以部署多个应用,每个应用都存在很多类,而且全类名可以相同,所以tomcat会为每一个应用生成一个类加载器实例,河阳就能达到应用之间的类隔离,不会冲突。而且tomcat自定义加载器实现了热加载功能。
热加载器:热加载是指在应用程序运行过程中,动态更新已加载的类文件,使得新的类定义能够立即生效,而无需重启应用程序
6.修改string变量,其引用的指向不变
Stringbuilder不行,创建新的变量
replace也不行,原变量不变
可以通过反射
String s=new String("abc");
Field value=s.getClass().getDeclaredField("value");
value.setAccessible(true);
value.set(s,"abcd".toCharArray());
7.string类型是否相等
String s1=new String("abc");//创建了两个对象,一个是abc对象,存放在常量池中,另一个是string对象放在堆中。
String s2="abc";//s2的值就是从常量池中获取
//s1!=s2
String s3=s1.intern();//string对象的intern对象,首先会检查字符串常量池是否存在abc,如果存在则返回字符串引用,如果不存在,则把abc添加到字符串常量池中,返回该字符串常量的引用
//s2==s3
8.Integer包装类
Integer类中,存在一个静态内部类IntergerCache,在类加载的时候,将-128-127数字提前生成Integer对象,缓存在数组中。
9.初始化
当有父类时,完整的初始化顺序为:父类静态变量(静态代码块)->子类静态变量(静态代码块)->父类非静态变量(非静态代码块)->父类构造器 ->子类非静态变量(非静态代码块)->子类构造器 。
10.双亲委派模型
一个类加载器收到类加载的请求,不会先加载此类,而是把这个请求委派给父类加载器去完成,最终都会传送到顶层的启动类加载器中,只有当父加载器反馈总结无法完成这个加载请求时,自加载器才会尝试自己去加载。
11.线程非安全与安全
ArrayList和Vector StringBuilder和StringBuffer HashMap和Hashtable
12.HashMap底层数据结构
jdk1.8之后都是“数组+链表+红黑树”,挺高查找性能,链表时O(n),红黑树是O(logn)
13.链表与红黑树的转换
对于插入,默认情况是使用链表节点,当同一个索引位置的节点在新增后超过8哥,如果此时数组长度大于等于64,则会触发链表节点转红黑树节点;而如果数组长度小于64,则不会出发链表转红黑树,而是会进行扩容。
对于移除,当同一个索引位置的节点在移除后达到6个,并且该索引位置的节点为红黑树节点,会触发红黑树节点转链表节点。
14.线程池的好处
- 降低资源消耗:通过重复利用已创建的线程,降低线程创建和销毁造成的消耗
- 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行
- 增加线程的可管理型:线程是稀缺资源,使用线程池可以进行统一分配,调优和监控
15.线程池的核心属性
threadFactory(线程工厂):用于创建工作线程的工厂
corePoolSize(核心线程数):当线程池运行的线程少于corePoolSize时,将创建一个线程来处理请求,即使其他工作线程处于空间状态。
workQueue(队列) :用于保留任务并移交给工作线程的阻塞队列。
maxmumPoolSize(最大线程数):线程池允许开启的最大线程数
handler(拒绝策略):1)线程池运行状态不是RUNNING 2)线程池已经达到最大线程数,并且阻塞队列已满时。
keepAliveTime(保持存活时间):如果线程池档期那线程数超过corePoolSize,则多余的线程空闲时间超过KeepAliveTime时会被终止。
16.死锁
(1)互斥条件(2)请求和保持条件(3)不可剥夺条件(4)环路等待条件
17.wait()和sleep()方法的区别
来源不同:sleep()来自Thread类,wait()来自Object类
对同步锁的影响不同:sleep()不会释放同步锁。wait()会释放同步锁
使用范围不同:sleep()可以在任何地方使用。wait()只能在同步控制方法或者同步控制块里面使用,否则会抛异常
恢复方式不同:两者会暂停当前线程,sleep()在时间到了之后会重新恢复:wait()则需要其他线程调用同一对象的notify()/nofityAll()才能重新恢复。
18.sleep()方法和yield方法的区别
sleep()方法后进入超时等待状态,而执行yield()方法后进入就绪状态
sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程运行的己会;
yield()方法只会给相同优先级或更高优先级的线程以运行的机会
19.线程的join()方法
用于等待当前线程终止。如果一个线程A执行了threadB.join()语句;就是当前线程A等待threadB线程终止之后才从threadB.join()返回继续往下执行自己的代码。
20.try,catch,finally
- 在return前会先执行finally语句块
public static int test1() {
try {
return 2;
} finally {
return 3;
}
}
结果是3
- 在执行finally之前,JVM会先将i结果暂存起来,然后finally执行之后,会返回之前暂存的结果。
public static int test1() {
int i = 0;
try {
i = 2;
return i;
} finally {
i = 3;
}
}
//结果是2
21.浅拷贝与深拷贝
浅拷贝:对于基础数据类型,直接复制数据值;引用数据类型,只是复制引用地址,还是指向同一个对象
深拷贝:对于举出数据类型,直接复制数据值;引用数据类型,开辟新的内存空间,复制一个一摸一样的对象,修改值不会影响原来的对象。