反射相关
类的加载机制
classLoader(双亲委派)加载部分.class->类信息放入元空间->生成唯一Class对象放进堆内存
反射
目的:代码执行时,能够获取类的方法名、属性,以及给属性赋值
原理:Class对象相当于元空间的类信息的镜像,new直接通过元空间的类对象(有指针指向元空间类信息),反射是通过镜像生成对象。
实现:
public class Animal {//default 子类不可跨包, protect可以子类可跨包, private子类可以继承
public float run(float distance){
return distance;
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Animal animal1 = new Animal();
animal1.run(2);
//类的反射对象
Class clz = Class.forName("Animal");
//方法的反射对象
Method method = clz.getMethod("run", float.class);
//构造方法的反射对象
Constructor constructor = clz.getConstructor();
//通过反射实例化对象
Object object = constructor.newInstance();
//通过放射调用方法
Object distance = method.invoke(object, 3);
}
}
三种获取Class的方式
- 类全限定名获取:Class class = Class.forName(“com.xxxx.xx”);
- 具体类获取:Class class = 类.Class;
- 对象获取:Class class=object.getClass();
参考:https://www.bilibili.com/video/BV13q4y1s7U8?spm_id_from=333.337.search-card.all.click&vd_source=ccd2ca4db2c7f66c6f0ab6dd058b6208
动态代理
目的:想在所有被代理的方法前加一个方法,动态代理可以方便实现
原理:所有实现被代理对象的方法,都会转为执行invoke中的方法
实现:类似aop
注解
原理:注解只是一个标志,反射查看类信息时发现注解就进行相应操作。
实现:
1.使用Spring中定义好的注解
定义类,使用注解注册以及赋值
@Component
@Data
public class Student {
@Value("小明")
private String name;
@Value("18")
private int age;
}
定义配置,扫描包范围
@Configuration
@ComponentScan("com.test.bean")
public class MainConfiguration {
}
打印类信息
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
System.out.println(context.getBean(Student.class));
}
}
结果
Student(name=小明, age=18)
2.自定义注解实现@Component(spring工厂)以及@Value(spring依赖注入)
注解定义-》注解使用-》@MyComponent表示通过反射生成对象@MyValue表示通过反射赋值(更改属性权限)
注解定义
@Retention(RetentionPolicy.RUNTIME) //运行时使用
@Target(ElementType.TYPE) //作用于类上
public @interface MyComponent {
}
@Retention(RetentionPolicy.RUNTIME) //运行时使用
@Target(ElementType.FIELD) //作用于属性上
public @interface MyValue {
String value(); //放置给的属性值
}
类更改为自己的注解
@MyComponent
public class Student {
@MyValue("小明")
private String name;
@MyValue("18")
private int age;
}
寻找@MyComponet
Class<Student> studentClass = Student.class;
MyComponent myComponent = studentClass.getAnnotation(MyComponent.class);//这里开始只有类,只能找类注解,属性注解需要获取属性后,再获取属性上的注解
实现@MyComponent的方法
if(myComponent!=null){
Constructor<Student> constructor = studentClass.getConstructor(null);
Student student = constructor.newInstance();
}
寻找@MyValue
Field[] declareFields = studentClass.getDeclaredFields();// getFields()只能获取公有属性,getDeclaredFields()获取所有属性
for(Field field : declareFields){
MyValue myValue = studentClass.getAnnotation(MyValue.class);
}
实现@MyValue的方法
if(myValue != null){
declareField.setAccessible(true); // 变量为private,需要修改属性权限
// 如果注解中的值为int需要进行转换
if(declareField.getType().getName().equals("int")){
declareField.set(student, Integer.parseInt(myValue.value()));//将注解中的值,赋值给创建出的目标对象
}
else{
declareField.set(student, myValue.value());//将注解中的值,赋值给创建出的目标对象
}
}
完整的测试类
public class Test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Student> studentClass = Student.class;
MyComponent myComponent = studentClass.getAnnotation(MyComponent.class);
if(myComponent!=null){
Constructor<Student> constructor = studentClass.getConstructor(null);
Student student = constructor.newInstance();
Field[] declareFields = studentClass.getDeclaredFields();// getFields()只能获取公有属性,getDeclaredFields()获取所有属性
for(Field declareField : declareFields){
MyValue myValue = declareField.getAnnotation(MyValue.class);//属性注解,就要在属性上去寻找
if(myValue != null){
declareField.setAccessible(true); // 变量为private,需要修改属性权限
// 如果注解中的值为int需要进行转换
if(declareField.getType().getName().equals("int")){
declareField.set(student, Integer.parseInt(myValue.value()));//将注解中的值,赋值给创建出的目标对象
}
else{
declareField.set(student, myValue.value());//将注解中的值,赋值给创建出的目标对象
}
}
}
System.out.println(student);
}
}
}
结果
Student(name=小明, age=18)
IOC(@Service,@mapper, BeanFacotory)
目的:将使用new创建对象的高耦合,转变为从容器中获取的低耦合
原理(工厂+xml+反射):加载ApplicationContext容器,从容器中getBean获取
service和mapper举例,直接建立工厂,从工厂中getmapper,降低了service和mapper的耦合,但是增大了service和工厂的耦合。不直接在工厂中new,而是通过xml文件中Bean的id找到mapper的class类,通过反射得到对象。最后,就可以通过getBean(意味着通过xml字符串指定,而不是直接new)得到了,而不是getXXmapper这样的强耦合。
实现:
- xml文件
- 注解
- 注解+扫包
AOP(@Trasaction)
目的:分离业务代码与非业务代码,让主代码块只关心核心业务的编写
原理:Bean的创建过程中,会在BeanPostProcessor中实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WAjYQzFF-1664516979229)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\QQ截图20220831082221.png)]
实现:
目标对象
public class Student {
String name;
int age;
public void say(){
System.out.println("天气真好");
}
}
切面对象
public class AopAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(method);
System.out.println(args);
System.out.println(target);
}
}
xml文件设置代理关系
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean name="student" class="com.test.bean.Student"/>
<bean name="beforeAdvice" class="com.test.aop.AopAdvice"/>
<aop:config>
<aop:pointcut id="stu" expression="execution(* com.test.bean.Student.say())"/>
<aop:advisor advice-ref="beforeAdvice" pointcut-ref="stu"/>
</aop:config>
</beans>
主方法
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
Student student = (Student) context.getBean("student");
student.say();
}
}
运行结果
public void com.test.bean.Student.say()
[Ljava.lang.Object;@5884a914
com.test.bean.Student@50378a4
天气真好
拓展:在Spring框架中,PostProcessor也用到了AOP。当一个Bean实现PostProcessor接口后(PosrProcessor.isAssignableFrom(Class)),就实例化一个PostProcessor对象,并将Bean加入到一个列表PostProcessList。在这个列表中的Bean,会根据不同的BeanName添加不同的前置或后置操作。因此,在CreateBean(创建Bean)的方法中,初始化Bean前后会遍历PostProcessList,并调用其中的方法。
spring事务
基本原理
目的:spring使用数据库中的事务
原理:添加@Transactional可自动开启事务,会关闭自动提交,作为Bean,有异常就回滚
实现:
public class TestServiceImp implements TestService{
TestMapper testMapper;
@Transactional
@Override
public void test() {
testMapper.insert();
testMapper.select();
}
}
如果不使用@Transaction相当于
public class TestServiceImp implements TestService{
TestMapper testMapper;
@Override
public void test() {
TransactionManager transactionManager;
try {
testMapper.insert();
testMapper.select();
transactionManager.commit();//提交
}catch(){
transactionManager.rollback();//回滚
}
}
}
特性
原子性、一致性(数据库保持一致)、隔离性、持久性(写入缓存文件,缓存文件修改后,更新到数据库。如果未写入,可以读取undo log)
持久性:
do log:三种设定:0(commit后值写入Log)、1(默认,commit后直接写入磁盘)、2(commit后写入系统缓存,有系统决定写入磁盘时间)
undo log:有指针指向未进行事务前的状态
隔离级别
两个事务处于并行状态
读另一个未提交(会读取到未提交的insert,同一事务读取到整张表不同的查询结果);读另一个已提交(同一事务读取到另一个update数据,出现幻读);同一事务可重复读保持数据不变(默认,一个事务的commit不影响当前事务的反复查询结果。正因如此,会出现重复插入);串行。
MVCC
数据库中数据多版本的控制
效果:当一个数据正在被修改(update但未提交,此时加了行锁),一个事务过来读取不会阻塞,而是去undo log中读取历史版本(属于快照读,区别与当前读会读取最新版本,insert、upate、delete、select for update会使用当前读),readView控制哪个版本对当前事务可见。
传播机制
A调用B,在B的角度理解
1.REQUIERD:如果A没有事务就新建,A有事务B就加入
2.SUPPORTS:如果A没有事务,B就以非事务方式执行
SpringBoot
自动装配
spring starter
起步依赖:
会导入其余和某个依赖相关的Jar包
自动装配:
工厂、容器->配置导入->EableAutoConfiguration
@Configuration让spring IOC容器管理Bean的实例工厂
@Bean返回的对象注册到spring容器中(Bean的生命周期?)
@ConfigurationProperties将配置参数信息yaml、properties文件导入到Bean中
@EnableAutoConfiguration让Bean能够被发现
BeanFacory和FactoryBean(定制Bean的生产过程)
Bean的生命周期
实例化->属性填充(三级缓存)->调用Aware相关(完成BeanFactory,BeanName, BeanClassLoader设置)->postProcessor前置->postProcessor后置->注销
简单Bean周期:
扫描
每个Beand对应一个BeanDefinetion对象保存Bean属性(Class, scope)。产生原因是通过一个字符串找到类困难,并且多次创建需要多次解析麻烦。
1.实例化
2.填充属性(对象)
3.填充属性
4.Aop
5.放入单例池(ConcurrentHashMap<BeanName, 对象>)
单例模式下的Bean循环依赖问题:
Aservice需要Bservice ,Bservice需要Aservice,但是此时单例池中还未放入
一级缓存:单例池
二级缓存:Aop完成的对象,防止B、C均需要单例A,创建了两次,因此B完成Aop后放入二级缓存,C再创建A时就需要进入二级缓存找。
三级缓存:当二级缓存中没有,需要Aop时,需要原始对象,来自上一步实例化时使用lambda表达式将BeanName,BeanDefinition(是否需要aop)到三级缓存,这样执行lambda表达式时相当于进行了Aop。
数据库相关
MySql
原理
show index form student;(查看索引)
索引添加:主键、唯一会添加索引、key()给指定字段添加索引
select * from student where id=“1”;(id有索引查询很快)
select * from student where name=“xiaomiao”;(name没有索引查询很慢)
提高查询效率,降低了写操作,但无所谓,大部分为查询
索引类型
普通(有重复数据)、唯一、主键(特殊唯一、唯一标识一条记录)、组合、全文(解决是否包含)
普通索引:叶子节点存储的是主键索引
全文索引:
组合索引:最左前缀原则:(name,age,sex)组成索引后,相当于建立了name;name、age;name、age、sex(严格从左到右建立的索引,根据name建立的age,根据age建立的sex)。查询(“aaa”,18,“女”)的位置,会先查找到相同的name,再查找age、sex。如果只查找age=18,sex=“男”会出现宕机,因为是根据name建立的索引。
索引失效
模(模糊查询like不用%)型(类型错误)数(内部函数)空(不限制Not null)运(索引列±*/)最(最左缀)快(全表扫描更快)
聚集索引、非聚集索引
主键为聚簇索引(行数据放在叶子节点),非主键为非聚簇索引。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JAKoZFjH-1664516979232)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\聚簇、非聚簇索引.png)]
left join right join inner join
结果集去重
distinct a,b,c ;gounpby a,b,c
sql优化
什么样的sql需要优化-》慢查询日志设置查询超过5s-》EXPLAIN 找执行计划(type:system>const>ref>all,rows:查询的行数)-》索引失效
redis
参考:https://www.bilibili.com/video/BV1aU4y1Z71c?p=1&vd_source=ccd2ca4db2c7f66c6f0ab6dd058b6208
数据结构
使用C语言编写的redis
字符串:由于C语言中字符串长度查询为O(n),无法存特殊字符,扩容不便。redis中字符串使用struct封装,并添加长度等属性。
哈希表:链地址法,链中存储entry为键值对
集合:哈希表的键唯一
有序集合:依据为链表,在链表中建立二级、三级…索引形成level
缓存
缓存击穿:一条数据缓存中没有,要到数据库中查询。设置锁拦截。
缓存雪崩:多条数据缓存中没有。设置锁,热点数据提前更新到缓存。
缓存穿透:多次查询缓存、数据库中均没有的数据。布隆方法为每个数据库的数据,利用多个hash计算出1、0组成的数组,查询前数据通过相同hash计算出的数组值是否符合,过滤掉大量无效查询。
缓存淘汰:LRU
过期删除:防止一直读旧数据。主动删除(一段时间后删除所有)+惰性删除(一般情况下,查看数据是否过期,过期去数据库找)
缓存一致:数据库可以利用双删。在写入操作前后均删除缓存。防止只在写入前进行删除后,有线程把旧数据读到缓存中,导致后序线程均从缓存中读到旧数据。
集群
主从复制:主库复制写,从库负责读。从库可以全量复制主库中数据,一般只复制近期主库中执行的操作。
哨兵:主库、从库会每个10s向哨兵发送自己的状态。主库出现问题,哨兵会挑选出主库。
cluster:不同服务器存储不同的数据(主库、从库数据相同),会有槽位信息记录在每台服务器上。
并行计算相关
多线程
https://www.bilibili.com/video/BV1V4411p7EF?p=1
线程生命周期(5个):
创建(new)—》就绪(start后)《-》阻塞(调用wait()/sleep()会切换)《-》运行(start后并在cpu请求到资源)-》死亡
多线程创建方式:
继承Thread(同样实现了Runable接口,因此可以用静态代理的方式,传入Ruable对象,只实现了Run方法的Ruable类可以用匿名内部类进行创建,进一步使用lambda表达式精简)实现Runable(主要采取此方式,可以传入多个线程)、Callable(提供返回值,以及开启结束服务)
抢票:
public class Test {
static class GetTicket implements Runnable{
static int ticketNum = 10;
@Override
public void run() {
while (true){
if(ticketNum<=0)break;
System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--+"号票");
}
}
}
public static void main(String[] args) {
GetTicket getTicket = new GetTicket();
Thread t1 = new Thread(getTicket,"学生");
Thread t2 = new Thread(getTicket,"老师");
Thread t3 = new Thread(getTicket,"黄牛");
t1.start();
t2.start();
t3.start();
}
线程设置
线程停止:建议设置标志位
public class Test_stop implements Runnable{
//标志位
boolean flag = true;
@Override
public void run() {
while (flag){
System.out.println("线程进行中");
}
}
//改变标志位的方法
public void changeFlag(boolean flag){
this.flag=flag;
}
public static void main(String[] args) {
Test_stop test_stop = new Test_stop();
new Thread(test_stop).start();
for (int i = 0; i < 100; i++) {
System.out.println(i);
if(i==20){test_stop.changeFlag(false); }//当i==20时test_stop线程结束
}
}
}
休眠:Thread.sleep()
礼让:Thread.yied()回到同一起跑线,不一定成功,取决于cpu
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
//礼让成功结果为:a开始 b开始 a结束 b结束
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
Thread.yield();//如果没有礼让成功,就会将一个线程从开始到结束执行完
System.out.println(Thread.currentThread().getName()+"结束");
}
}
线程强制执行Thread.join(),加入进的线程要执行完
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("VIP来了,执行第"+i+"次");
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main执行第"+i+"次");
if(i==100){
thread.join();//强势介入,main在100之后就要等thread执行完才能执行
}
}
}
}
Thread.status线程状态(死亡后不能再次启动)
Thread.setPriority,范围1-10,默认为5
Thread.setDaemon,虚拟机不会等待守护线程结束
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread godThread = new Thread(god);
godThread.setDaemon(true);//设置守护
godThread.start();//守护线程
new Thread(you).start();//当该线程结束后,虚拟机还会将守护线程再跑一会
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("开心活着"+i+"年");
}
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝守护者");
}
}
}
锁
锁住某一段代码,提供一个对象作为锁的本身。锁住原理是获取每个对象的moniter监视器
重排序:代码指令会调整顺序,由于重排序,下列代码可能输出B,因为可能将b=1放到a=1前面。
public class Main {
private static int a=0;
private static int b=0;
public static void main(String[] args) throws FileNotFoundException {
new Thread(()->{
a=1;
b=1;
}).start();
new Thread(()->{
if(b==1){
if(a==1){
System.out.println("A");
}
else {
System.out.println("B");
}
}
}).start();
}
}
volatile:
解决问题:对所有线程保持可见性,会将变量放到主内存
不保证原子性,还是会出现两个线程同时操作。
防止指令重排(之前的指令执行,之后的指令不执行)
sychonized底层实现(对象锁,传入的类.class实际就是个Class对象,联想到object具有的几个方法Clone()(浅克隆,只克隆地质),toString(),wait()(释放锁,sleep不释放锁),notify(),equals(), hashcode(),finalize()(释放资源)。当sychornized锁方法的时候,默认锁住this对象。
抢票:
public class Test {
static class GetTicket implements Runnable{
static int ticketNum = 10;
@Override
public synchronized void run() {//加入synchornized关键字防止同一张票多人抢、抢到负数
while (true){
if(ticketNum<=0)break;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--+"号票");
}
}
}
public static void main(String[] args) {
GetTicket getTicket = new GetTicket();
Thread t1 = new Thread(getTicket,"学生");
Thread t2 = new Thread(getTicket,"老师");
Thread t3 = new Thread(getTicket,"黄牛");
t1.start();
t2.start();
t3.start();
}
}
银行取钱:
public class TestBank {
public static void main(String[] args) {
Account account = new Account(50);
DrawMoney you = new DrawMoney(account, 50, "you");
DrawMoney girlfriend = new DrawMoney(account, 50, "girlfriend");
you.start();
girlfriend.start();
}
}
class Account{
int money=1000;
public Account(int money){
this.money=money;
}
}
class DrawMoney extends Thread{
Account account;
int myMoney;
int drawMoney;
public DrawMoney(Account account,int drawMoney,String name){
super(name);
this.drawMoney=drawMoney;
this.account=account;
}
@Override
public void run() {//在这里加synchronized无用,锁的是DrwMoney,还是会操作未加锁的account,需要使用同步块
synchronized (account){
if(account.money<=0){
//extends Thread因此this.name==Thread.currentThread.getName()
System.out.println("钱不够了,"+this.getName()+"取不了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//取钱
account.money = account.money-drawMoney;
System.out.println(this.getName()+"取走了"+drawMoney+"还剩:"+account.money);
}
}
}
线程不安全ArrayList:CopyOnWriteArrayList会复制个transient数组(不被序列化,只在内存中)写,并锁住add操作,此时另外线程会读取旧数组
public class TestArrayList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> strings = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
strings.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);//这里必须休息,不然主线程跑完了打印出的string.size是不完整的
System.out.println(strings.size());//大小不足1000,因为不同线程会写入同一位置,CopyOnWriteArrayList就是1000
}
}
死锁:
互相持有对方的锁,jps->jstack查找,以下代码不会结束
public class Main {
public static void main(String[] args) throws FileNotFoundException {
Object o1 = new Object();
Object o2 = new Object();
Thread th1 = new Thread(()->{
synchronized (o1){
try {
Thread.sleep(1000);//并未释放o1锁
synchronized (o2){
System.out.println("线程1结束");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread th2 = new Thread(()->{
synchronized (o2){
try {
Thread.sleep(1000);
synchronized (o1){
System.out.println("线程2结束");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
th1.start();
th2.start();
}
}
lock、condition:下列代码执行顺序:线程1进入等待->线程2唤醒其它线程->线程2结束->线程1等待结束
public class Main {
public static void main(String[] args) throws FileNotFoundException {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();//可以给锁变换状态
new Thread(()->{
lock.lock();//必须持有锁才能进行wait
System.out.println("线程1进入等待");
try {
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程1等待结束");
}).start();
new Thread(()->{
lock.lock();
System.out.println("线程2唤醒其它线程");
condition.signal();
System.out.println("线程2结束");
lock.unlock();
}).start();
}
}
ABA问题:
修改值时,只会比较当前值和之前值是否一致,一致就进行修改。但是会出现另一个线程已经重新赋相同的值。
juc解决方法:记录版本号
AQS:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8bzIuQTs-1664516979235)(C:\Users\DeLL\AppData\Roaming\Typora\typora-user-images\image-20220927151614499.png)]
几种锁状态的切换
无锁->偏向锁(对象记录线程的指针)->自旋(出现其余锁竞争)->轻量级锁(对象记录线程Lock Record指针)->重量级锁(大量的自旋消耗cpu资源,不如将轻量级锁改为重量级锁,其余线程为wait()不消耗cpu)
轻量级锁:对象中有mark word,虚拟栈中有Lock Record。CAS:当对象中的Mark Word为空时,创建Lock Record,并将mark word填入自己的Lock Record(diaplaced mark word)。不为空时,拿到mark word中的值,与自己的Lock Record进行比较,如果一样,就执行同步代码块,如果不一样就说明已经有线程占用,升级为重量级锁。
线程池
public class Main {
public static void main(String[] args) throws FileNotFoundException, InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,//核心2,最大4,意味这最多再新创建2个线程
1, TimeUnit.SECONDS,//非核心存活1s
new ArrayBlockingQueue<>(2));//等待2
//最大可以塞满8个线程,因为有2个线程在队列中,则同时可以执行6个线程
for (int i=0; i<6; i++){
int finalI = i; //当前任务
executor.execute(()->{
System.out.println("线程:"+Thread.currentThread().getName()+"执行任务:"+finalI);
try {
TimeUnit.SECONDS.sleep(1);//Thread.sleep()的替身
System.out.println("线程:"+Thread.currentThread().getName()+"结束任务:"+finalI);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
System.out.println("线程数量:"+executor.getPoolSize());//线程数量
TimeUnit.SECONDS.sleep(3);//等待所有执行完
System.out.println("线程数量:"+executor.getPoolSize());
executor.shutdown();
}
}
运行结果
线程数量:4
线程:pool-1-thread-1执行任务:0
线程:pool-1-thread-4执行任务:5
线程:pool-1-thread-2执行任务:1
线程:pool-1-thread-3执行任务:4
线程:pool-1-thread-1结束任务:0
线程:pool-1-thread-4结束任务:5
线程:pool-1-thread-1执行任务:2
线程:pool-1-thread-4执行任务:3
线程:pool-1-thread-2结束任务:1
线程:pool-1-thread-3结束任务:4
线程:pool-1-thread-1结束任务:2
线程:pool-1-thread-4结束任务:3
线程数量:2
原理
频繁矿建线程,浪费资源
核心线程-》等待队列-》创建新线程-》拒绝
线程池种类
JVM相关
原理
内存模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LX2RibEF-1664516979236)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\内存模型.png)]
jvm栈会为每个函数开辟空间,函数结束运行后清除该空间,但是不能清除堆中空间,因此使用到GC
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8kxr5hPH-1664516979238)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\内存模型值.png)]
引用类型也是值传递,不过传递的是地址(int)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XK8iBstF-1664516979239)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\内存模型引用类型.png)]
垃圾回收
什么对象会被回收(不会再使用)?
引用计数:有一个地方引用+1;离开作用域或设置为null-1,当为0时引用失效。但循环引用a=b=null时,虽然两者均无用,但相互引用让计数器不会为0。
可达性:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wxw9BsuT-1664516979241)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\GC可达性算法.png)]
GCroots:
jvm栈中引用(正在使用)、元空间引用(全局)、本地方法栈引用(c++)。堆区对象和GCroots有直接或间接(被GCroots引用的对象引用)联系,就不能被删除。
分代管理:经过几次垃圾回收,都没被判定为可回收,就放在一个区域不再进行回收。
如何进行回收?
标记-》清除:标记清除后连续内存出现空隙(CMS老年代)
标记-》复制:内存空间分为两半,存活对象复制到另一半,解决了空隙问题。但如果大量对象为存活对象(回收不了多少),浪费了空间。(parNew用于新生代)
标记-》整理:将存活对象放在前面,回收对象放在后面,每次清除后面。解决了空闲浪费,但修改对象位置,效率低。
基础问题
byte 1;short 2;int 4;long 8;float 4;double 8;boolean 1;char 2
集合类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivZ1e3zp-1664516979242)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\集合.png)]
arraylist:可以调整大小的数组实现
堆栈实现
hashset
利用hash值判断位置,再比对值。
concrrentHashMap线程安全实现
hash冲突的解决方法:再哈希;链地址(hashmap使用的方法,冲突后变成链表);开放地址(探索周围位置);公共溢出区
hash线程安全
abstract和interface
abstract: is a,只能继承一个,可以有私有变量与方法
interface: like a, 可以实现多个,不能又私有变量与方法
==和equals
public class TestEquals {
public static void main(String[] args) {
String a = "123";
String b = new String("123");
String c = "123";
System.out.println(a==b); //false, 此时比较对象地址
System.out.println(a==c);//true, 此时比较值
System.out.println(a.equals(b));//true 此时比较值
System.out.println(a.equals(c));//true 此时比较值
}
}
B树、B+树
B树是基于二叉搜索树而来(非叶子节点有数据),B+树是基于分块思想而来。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqJiNyFM-1664516979243)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\B树.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjcgU8Wm-1664516979244)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\B+树.png)]
单例模式实现方式
选择排序、插入排序
记-》整理:将存活对象放在前面,回收对象放在后面,每次清除后面。解决了空闲浪费,但修改对象位置,效率低。
基础问题
byte 1;short 2;int 4;long 8;float 4;double 8;boolean 1;char 2
集合类
[外链图片转存中…(img-ivZ1e3zp-1664516979242)]
arraylist:可以调整大小的数组实现
堆栈实现
hashset
利用hash值判断位置,再比对值。
concrrentHashMap线程安全实现
hash冲突的解决方法:再哈希;链地址(hashmap使用的方法,冲突后变成链表);开放地址(探索周围位置);公共溢出区
hash线程安全
abstract和interface
abstract: is a,只能继承一个,可以有私有变量与方法
interface: like a, 可以实现多个,不能又私有变量与方法
==和equals
public class TestEquals {
public static void main(String[] args) {
String a = "123";
String b = new String("123");
String c = "123";
System.out.println(a==b); //false, 此时比较对象地址
System.out.println(a==c);//true, 此时比较值
System.out.println(a.equals(b));//true 此时比较值
System.out.println(a.equals(c));//true 此时比较值
}
}
B树、B+树
B树是基于二叉搜索树而来(非叶子节点有数据),B+树是基于分块思想而来。
[外链图片转存中…(img-VqJiNyFM-1664516979243)]
[外链图片转存中…(img-tjcgU8Wm-1664516979244)]