Java知识库,程序员必须Mark!
程序员文章站
2022-04-20 11:42:31
...
1. 若JVM进程中,只剩下后台线程,则该进程就结束了。可以使用setDaemon(true)将线程设置为后台线程。
2. 使用线程的join()方法,可以等待多个线程子任务执行完成后,进行合并结果的操作;但是join只是对线程单纯的顺序join,但是这个顺序不一定是线程真正结束的顺序,而CompletionService可以按照线程结束后的顺序给我们返回结果。另外,一个更大的区别在于线程的Join操作是针对线程的合并操作,这就意味着为每个任务创建对应的线程,如果任务数很多将导致创建大量的线程来处理,而CompletionService是基于线程池的任务,无论任务数量多少,线程的数量是可以控制的。
3. 线程是基于栈的,可以使用getStackTrace()方法,查看当前线程的栈轨迹。
4. 栈空间是每个线程私有的空间,当调用某方法时将给该私有栈分配空间,在方法内部再调用方法还会继续使用相应的栈空间,方法返回时收回相应的栈空间(不论是否抛出异常)。这块空间通常也叫做“工作内存”,堆空间也叫做“主内存”。
5. 普通变量的修改并不一定立即写回到主存,而线程读取时也不需要每一次都从主存中读取;
6. 使用volatile修饰变量,可以保证多线程中的共享变量始终是可见的(但这并不保证volatile引用对象内部的属性是完全可见的);
volatile还有一个作用就是防止相关性代码的重排序,从指令级别达到了轻量级锁的目的。
7. 线程池初始化时是没有任何线程的,有任务是才会创建线程;而数据库连接池初始化时,是已经存有一定的连接了,否则用到时再创建会耗时的。
8. JDK动态代理在生成动态字节码时,并不是通过实现类创建子类的方式,而是通过类所拥有的接口列表来完成的。也就是说,生成的类和实际的类一点关系都没有,而是一个独立的类,只是方法名和接口的方法名完全一样。但是生成的代理对象初始化时,会传递一个handler参数,而这个handler里是包含实际实现类(target)的,所以代理类调用方法时,实际上是使用target调用方法的。
顺便说一下,Cglib动态代理是继承实际类生成子类,调用方法时也不是使用反射(反射效率相对较低),而是使用FastClass通过索引调用相应的方法的。
9. spring配置文件中component-scan配置中的base-package属性是要扫描的路径,spring会扫描相应classpath下所有与base-package相关的路径,包括jar包的路径,但是不会遍历jar包里的类,所以如果把加载的Bean放在jar包中,那么即使是base-package="*"也是扫描不到的。
10. 在web应用中,可以通过 ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()获取到HttpServletRequest对象。
11. Spring将Connection对象放入了一个ThreadLocal变量中,而且是以Map的方法存在的,Map的key是DataSource。在具有事务的方法中我们拿到的连接不是Connection本身,而是被代理的对象,所以两次获取到的Connection的HashCode是不同的。但是代理类对象中目标对象是相同的(上面也说过),都是原始的Connection连接,所以如果在同一个事务中,那么无论多少次获取出来的连接,其内部的target属性都是同一个。
12. 使用双重锁判定可以大幅降低锁的征用。比如:
13. 进程与线程的关系:
比如有一个大工厂,里面每一个车间都是一个‘进程’,车间里的工人就是‘线程’,车间里有一个‘公共厕所’。那么,当工人上厕所时要锁上门,人少时直接就完事了;但人多时只能在厕所门口等,等里面的人出来才能进去(锁着门呢)。当然这只是一般的情况,并没有考虑优先级等。
2. 使用线程的join()方法,可以等待多个线程子任务执行完成后,进行合并结果的操作;但是join只是对线程单纯的顺序join,但是这个顺序不一定是线程真正结束的顺序,而CompletionService可以按照线程结束后的顺序给我们返回结果。另外,一个更大的区别在于线程的Join操作是针对线程的合并操作,这就意味着为每个任务创建对应的线程,如果任务数很多将导致创建大量的线程来处理,而CompletionService是基于线程池的任务,无论任务数量多少,线程的数量是可以控制的。
3. 线程是基于栈的,可以使用getStackTrace()方法,查看当前线程的栈轨迹。
4. 栈空间是每个线程私有的空间,当调用某方法时将给该私有栈分配空间,在方法内部再调用方法还会继续使用相应的栈空间,方法返回时收回相应的栈空间(不论是否抛出异常)。这块空间通常也叫做“工作内存”,堆空间也叫做“主内存”。
5. 普通变量的修改并不一定立即写回到主存,而线程读取时也不需要每一次都从主存中读取;
6. 使用volatile修饰变量,可以保证多线程中的共享变量始终是可见的(但这并不保证volatile引用对象内部的属性是完全可见的);
volatile还有一个作用就是防止相关性代码的重排序,从指令级别达到了轻量级锁的目的。
7. 线程池初始化时是没有任何线程的,有任务是才会创建线程;而数据库连接池初始化时,是已经存有一定的连接了,否则用到时再创建会耗时的。
8. JDK动态代理在生成动态字节码时,并不是通过实现类创建子类的方式,而是通过类所拥有的接口列表来完成的。也就是说,生成的类和实际的类一点关系都没有,而是一个独立的类,只是方法名和接口的方法名完全一样。但是生成的代理对象初始化时,会传递一个handler参数,而这个handler里是包含实际实现类(target)的,所以代理类调用方法时,实际上是使用target调用方法的。
顺便说一下,Cglib动态代理是继承实际类生成子类,调用方法时也不是使用反射(反射效率相对较低),而是使用FastClass通过索引调用相应的方法的。
9. spring配置文件中component-scan配置中的base-package属性是要扫描的路径,spring会扫描相应classpath下所有与base-package相关的路径,包括jar包的路径,但是不会遍历jar包里的类,所以如果把加载的Bean放在jar包中,那么即使是base-package="*"也是扫描不到的。
10. 在web应用中,可以通过 ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest()获取到HttpServletRequest对象。
11. Spring将Connection对象放入了一个ThreadLocal变量中,而且是以Map的方法存在的,Map的key是DataSource。在具有事务的方法中我们拿到的连接不是Connection本身,而是被代理的对象,所以两次获取到的Connection的HashCode是不同的。但是代理类对象中目标对象是相同的(上面也说过),都是原始的Connection连接,所以如果在同一个事务中,那么无论多少次获取出来的连接,其内部的target属性都是同一个。
12. 使用双重锁判定可以大幅降低锁的征用。比如:
class ObjInstance { //单例 private static ObjInstance oi = new ObjInstance(); private static User user; private ObjInstance() { } public static User getUserInstance() { if (user == null) {// 无锁判定 synchronized (oi) { if (user == null) {// 加锁判定 user = new User(1, "zs"); } } } return user; } }
13. 进程与线程的关系:
比如有一个大工厂,里面每一个车间都是一个‘进程’,车间里的工人就是‘线程’,车间里有一个‘公共厕所’。那么,当工人上厕所时要锁上门,人少时直接就完事了;但人多时只能在厕所门口等,等里面的人出来才能进去(锁着门呢)。当然这只是一般的情况,并没有考虑优先级等。
推荐阅读
-
为什么说 Java 程序员到了必须掌握 Spring Boot 的时候?
-
一个优秀的Java程序员必须了解GC机制
-
一个优秀的Java程序员必须了解GC机制
-
现在是不是Java程序员到了必须掌握spring boot的时候了?看看就知道了呀
-
Java 程序员必须掌握的 5 个注解!
-
Java程序员怎么不断进阶 必须要掌握哪些技能
-
Java程序员必须知道的Mysql优化方案
-
为什么说 Java 程序员到了必须掌握 Spring Boot 的时候?
-
现在是不是Java程序员到了必须掌握spring boot的时候了?看看就知道了呀
-
有人说程序员只会PHP还不够,必须要会JAVA,PYTHON,C++中的一种,是这样的么?