JAVA复习

基础

一、Java基础

(一) 面向对象的特征:封装、继承、多态

  1. 封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
  • 优点:隐藏实现细节,提供公共的访问方式,提高了代码的复用性,提高安全性。
  1. 多个类中出现相同的属性,可将内容抽取到一个单独的类中。单继承,多实现。
  • 优点:提高了代码的复用性,提高代码的维护性,让类与类之间产生了关系,为多态提供了前提。
  • 缺点:增强了类之间的耦合性。
  1. 多态:多态的前提是继承和方法重写(否则无意义),多态一个事物在不同时刻下表现出的不同状态,父类引用指向子类对象
  • 优点:提高了代码的维护性和扩展性
  • 缺点:不能使用子类特有的功能

(二) final,finally,finalize的区别

  1. final可以用来修饰类(表示该类不能被其他类继承,类中的所有成员方法都会默认定义为final方法),方法(方法不可被继承和更改)和变量(final成员变量表示常量,只能被赋值一次,赋值后不能被再改变)
  2. finally作为异常处理的一部分,只能被用在try/catch语句中,表示在这段语句最终一定会被执行
  3. finalize是java.lang.Object中的一个方法,垃圾回收器

(三) Exception、Error、运行时异常与一般异常有何异同

  1. Error是由JVM所侦测到的无法预期的错误,属于JVM层次的严重错误,是不可捕捉,无法采取任何恢复的操作
  2. Exception表示可恢复的或可捕捉到的异常,Exception主要提供两类主要异常:runtime exception和checked exception
  • checked exception:必须try/catch,必须处理
  • runtime exception:运行期异常,可以不处理,交由虚拟机处理

(四) 请写出5种常见到的runtime exception

  1. NullPointException - 空指针异常
  2. ClassCastException - 类型强制转换异常
  3. IllegalArgumentException - 传递非法参数异常
  4. ArithmeticException - 算术运算异常
  5. ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
  6. IndexOutOfBoundsException - 下标越界异常
  7. NumberFormatException - 数字格式异常
  8. UnsupportedOperationException - 不支持的操作异常
  9. IllegalAccessException - 没有访问权限异常

(五) int 和 Integer 有什么区别,Integer的值缓存范围

  1. Integer是int的包装类;int是基本数据类型;
  2. Integer变量必须实例化后才能使用;int变量不需要;
  3. Integer是对象的引用,指向new的Integer对象;int直接存储数据值;
  4. Integer默认值为null;int默认值为0;
  5. Integer默认缓存范围是-128~127

(六) 包装类,装箱和拆箱

  1. 包装类:Java是典型的面向对象编程语言,但把总基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特征–不携带属性,没有方法可以调用。Java为每种基本数据类型本别设计了对应的类,称之为包装类。
  • 实现int和Integer的相互转换
  • 将字符串转换为整数
  • 将整数转换为字符串
  1. 装箱和拆箱:将基本类型变成包装类称之为装箱,将包装类变为基本数据类型称之为拆箱,JDK1.5之后,增加了自动拆箱和自动装箱功能

(七) String、StringBuilder、StringBuffer

  1. String为字符串常量,StringBuilder和StringBuffer为字符串变量
  2. 运行速度快慢为:StringBuilder > StringBuffer > String
  3. StringBuilder是线程不安全的,StringBuffer是线程安全的
  4. String适用于少量字符串操作的情况;StringBuilder适用于单线程下的字符缓冲区的大量操作;StringBuffer适用于多线程下在字符串缓冲区进行大量操作的情况下

(八) 重载和重写的区别

  1. 方法重载:指同一个类中的多个方法具有相同的名字,但方法的参数的数量和类型不能完全相同
  2. 方法重写:存在父子类之间,子类定义的方法与父类的方法具有相同的方法名,相同的参数表和相同的返回值类型。
  • 子类不能重写父类中的final方法
  • 子类中必须重写父类的abstract方法

(九) 抽象类和接口的区别

  1. 抽象类和接口都不能被实例化,若要实现实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口必须指向实现所有接口方法的类对象。
  2. 抽象类需要被子类继承,接口需要被类实现
  3. 抽象类只能单继承,接口可以实现多个
  4. 接口只能做方法申明,抽象类既可以申明,也可以做方法实现
  5. 接口定义的变量只能是公共的静态常量,抽象类中的变量是普通变量
  6. 抽象类中的方法必须全部被子类实现,否则该子类只能是抽象类;接口中的方法也必须被全部实现,否则该类只能为抽象类
  7. 抽象方法只能申明,不能实现;接口是设计的结果,抽象类是重构的结果;
  8. 抽象类里可以没有抽象方法
  9. 如果一个类里有抽象方法,那么这个类只能是抽象类。
  10. 抽象方法要被实现,所以不能是静态,也不能是私有的。

(十) 反射的用途及实现

  1. 定义:当程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。 Java 不是动态语言,但是有一个动态相关的机制,称为反射。
  2. 用途:
  • 在运行期间,可以获取对象的类型、类型的方法、类型的属性、类型构造方法等等。让对象可以认识到自身的结构
  • 可以用来开发各种通用框架(Spring、MyBatis)
  1. 实现:
    (1) 用反射方式创建对象

     		类对象.newInstance();  // 创建一个新实例(对象)
    

    (2) 获取方法信息

     		类对象.getMethods();           		 // 获取所有公共方法(public),包括继承的
     		类对象.getDeclaredMethods();    // 获取本类的所有方法(public, private, protected, 不加)
     		类对象.getMethod(方法名, 参数类型);           			// 找公共方法, 包括继承的 
     		类对象.getDeclaredMethod(方法名, 参数类型);     // 找本类的,不包括继承的
    

    (3) 获取属性信息

     	类对象.getFields();         // 获取所有公共属性(包括继承的)
     	类对象.getDeclaredFields(); // 获取本类所有属性(private public protected 不加)(不包括继承的)
     	类对象.getField("属性名");
     	类对象.getDeclaredField("属性名");
    

    (4) 获取构造方法

     	类对象.getConstructors(); // 获取所有公共的构造方法
     	类对象.getDeclaredConstructors(); // 获取本类所有构造方法(private public protected 不加)
     	类对象.getConstructor(int.class); // 获取int参数类型的构造
     	类对象.getConstructor(); // 获得无参构造
    

    (5) 反射调用方法

     方法.invoke(对象, 参数); 
     // 缺点:调用复杂,效率低
     // 优点:可以调用私有方法,突破正常方法的限制, 经常用在开源框架中
    

(十一)自定义注解的场景及实现

  1. 元注解
    (1) @Retention - 定义注解大的生命周期
    (2) @Target - 注解用于什么地方
    (3) @Documented - 表示是否将注解信息添加在java文档中
    (4) @Inherited – 是否允许子类继承该注解
  2. 使用场景
    登陆、权限拦截、日志处理,以及各种 Java 框架(Spring中通过AOP给方法添加切面,通过反射来获取方法包含的注解)
  3. 实现
    (1) 定义一个自定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoLogin {
}

(2) 使用AOP定义切面,在指定位置添加注解
(3) 在springMvc配置文件中,做aop的配置工作
(4) 查看相关控制层代码

(十二)HTTP请求的GET与POST方式的区别

  1. get是从服务器获取数据,post是向服务器传送数据
  2. .get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTPpost机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到过程。
  3. .get:服务器端用Request.QueryString获取变量的值;post:服务器端用Request.Form获取提交的数据。
  4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。
  5. get安全性非常低,post安全性较高。

(十三)Session与Cookie区别

  1. Cookie以文本文件格式存储在浏览器中,而session存储在服务端
  2. cookie的存储限制了数据量,只允许4KB,而session是无限量的
  3. 我们可以轻松访问cookie值但是我们无法轻松访问会话值,因此它更安全
  4. 设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。

(十四)常用的JDK包

  1. java.lang:包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
  2. java.awt:包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
  3. java.applet:包含applet运行所需的一些类。
  4. java.net:包含执行与网络相关的操作的类。
  5. java.io:包含能提供多种输入/输出功能的类。
  6. java.util:包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。

(十五)MVC设计思想

model(模型) - view(视图) - controller(控制器)

  • 模型:封装业务逻辑。包含了业务数据的加工与处理以及相应的基础服务(为了保证业务逻辑能够正常进行的事务、安全、权限、日志等等的功能模块)
  • 视图:用来实现表示逻辑。展现模型处理的结果,提供相应的操作界面。
  • 控制器:用来协调模型与视图
    实现了模型的复用,耦合性低,模型返回数据可以交给不同视图来展现,可以用不同视图来访问同一个模型。代码的维护性更好,方便测试。

(十六)equals与==的区别

  1. ==:作用于基本数据类型的变量,比较其存储的“值”是否相等;作用于引用类型的变量,比较的是指向对象的地址。
  2. equals:默认比较的是引用类型的变量指向的对象地址值,重写equals()方法后比较的是指向对象的内容。

(十七)hashCode和equals方法的区别与联系

重写的equal()里一般比较的比较全面比较复杂,效率会比较低。有时不同的对象他们生成的hashcode也会一样,不一定可靠。
解决方法:先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等;如果hashCode()相同,再对比他们的equal(),如果equal()也相同,则表示这两个对象相同。

(十八)什么是Java序列化和反序列化,如何实现Java序列化.Serializable 接口的作用

  1. 序列化是一种用来处理对象流的机制,对象流就是将对象的内容进行流化(将对象转换成二进制)。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间,序列化是为了解决在对对象流进行读写操作时所引发的问题。
    把对象转换为字节序列的过程称为对象的序列化,把字节序列恢复为对象的过程称为对象的反序列化
  2. 实现:在需要被序列化的对象接口上实现Serializable。Serializable接口中没有需要实现的方法,只是为了标注该对象可以被实例化。

(十九)Object类中常见的方法,为什么wait notify会放在Object里边.

  1. toString():输出一个对象的地址字符串;可以重写toString()方法,获取对象的属性。
  2. equals():比较对象的引用是否指向同一块内存地址,可以重写equals()方法比较两个对象的内容是否相同。
  3. Object():默认的构造方法
  4. clone():创建并返回该对象的一个副本。
  5. finalize():当垃圾回收期确定不存在该对象的更多引用时,有对象的垃圾回收器调用此方法。
  6. getClass():返回一个对象的运行时类。
  7. hashCode():返回该对象的哈希码值。
  8. notify():唤醒在此对象监视器上等待的单个线程;notifyAll(),唤醒此对象监视器上等待的所有线程。
  9. wait():让当前线程等待,直到其他线程调用此对象的notify()方法或者notifyAll()方法;wait(long timeout),超过指定时间量;
    wait(long timeout, int nanos),被其他线程中断或者超过某个实际时间量。
  10. synchronized中的这把锁可以是任意对象,所以任意对象都可以调用wait()和notify();所以wait和notify属于Object。等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法是定义在object类中。JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。

(二十)Java的平台无关性如何体现

Java是解释性语言,源文件通过编译器成字节码文件(.class),再通过JVM来实现脱离平台这一性质。Java虚拟机面向编译器给其提供相同的接口,编译器只需生成Java虚拟机可以理解的代码,然后再不同平台的解释器来生成平台相应的机器码来执行Java程序。

(二十一)JDK和JRE的区别

  1. JDK是Java Development Kit的缩写,是Java的开发工具包,主要包含了各种类库和工具,包括client和server端的,需要配置环境变量,也包含了一个JRE.。
  2. JRE,运行java程序的环境,JVM,JRE里面只有client运行环境,安装过程中,会自动添加PATH。

(二十二)Java 8有哪些新特性

  1. Lambda表达式:允许将函数当成参数传递给某个方法,或者把代码本身当作数据处理。
  2. 接口的默认方法和静态方法:可以在接口中定义默认方法,使用default关键字,并提供默认的实现。所有实现这个接口的类都会接受默认方法的实现,除非子类提供的自己的实现。还可以在接口中定义静态方法,使用static关键字,也可以提供实现。
  3. 方法引用:
  • 构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;
  • 静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;
  • 特定类的任意对象方法引用。它的语法是Class::method。要求方法是没有参数的;
  • 特定对象的方法引用,它的语法是instance::method。要求方法接受一个参数,与3不同的地方在于,3是在列表元素上分别调用方法,而4是在某个对象上调用方法,将列表元素作为参数传入;
  1. 重复注解:同的注解在同一地方也可以声明多次。重复注解机制本身需要用@Repeatable注解。Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
  2. 扩展注解的支持:扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解。
  3. Optional:引入Optional类来防止空指针异常,Optional类实际上是个容器:它可以保存类型T的值,或者保存null。使用Optional类我们就不用显式进行空指针检查了。
  4. Stream:Stream API把函数式编程风格引入到Java中。
  5. Date/Time API:提供了新的java.time包,可以用来替代 java.util.Date和java.util.Calendar。
  6. JavaScript引擎Nashorn:Nashorn允许在JVM上开发运行JavaScript应用,允许Java与JavaScript相互调用。
  7. Base64:Base64编码成为了Java类库的标准。Base64类同时还提供了对URL、MIME友好的编码器与解码器。

二、Java常见集合

(一)List 和 Set 区别

  1. List:
  • 可以允许重复的对象;
  • 可以插入多个null元素;
  • 是有序容器,保证了每个元素的插入顺序;
  • ArrayList底层为数组,查找快,增删慢,县城不安全,效率高;
  • Vector底层数据结构为数组,线程安全,效率低;
  • LinkedList底层为链表,查找慢,增删快,线程不安全,效率高;
  1. Set:
  • 不允许重复对象;
  • 无序容器,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序;
  • 只允许一个 null 元素;
  • HashSet底层为哈希表,保证元素的唯一性;TreeSet底层是二叉树,可以对元素进行排序;

(二)Set和hashCode以及equals方法的联系

Set为无序不重复集合,HashSet底层为哈希表,依靠hashCode和equals()方法来判断两个对象是否相同。

(三)List 和 Map 区别

  1. List是存储单列数据的集合,Map是存储键和值这样的双列数据的集合,

  2. List中存储的数据是有顺序,并且允许重复;

  3. Map中存储的数据是没有顺序的,键不能重复,值是可以有重复的。

(四)HashMap 和 Hashtable 的区别

  1. HashMap线程不安全,效率高;Hashtable线程安全,效率低
  2. HashMap可以接受null作为键值(key)和值(value)

(五)HashSet 和 HashMap 区别

  1. HashMap实现了Map接口,Map接口对键值进行映射,存储键值对,Map中不允许出现重复的键(key)。使用put()向map中添加元素。
  2. Hash实现了Set接口,不允许集合中出现重复元素,仅存储对象,需重写hashCode()和equals()方法,使用add()向Set中添加元素。

(六)HashMap 和 ConcurrentHashMap 的区别

HashMap是线程不安全的,ConcurrentHashMap底层采用分段的数组+链表实现,线程安全。通过把整个Map分成N个Segment(分段),可以提供相同的线程安全,效率提高N倍,默认一般提升16倍。
ConcurrentHashMap是使用了锁分段技术来保证线程安全的。**锁分段技术:**将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。原来只能一个线程进入,现在能同时有16个写线程执行,并发性能提升。

(七)HashMap 的工作原理及代码实现,什么时候用到红黑树

  1. HashMap采用位桶+链表+红黑树实现。
  2. 首先有一个每个元素都是链表的数组,当添加一个元素(key-value)时,先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。
  3. 在 JDK1.8 中,HashMap中当 hash 碰撞之后写入链表的长度超过了阈值(默认为8),链表将会转换为红黑树。

(八)多线程情况下HashMap死循环的问题

  1. HashMap是采用链表解决Hash冲突,因为是链表结构,那么就很容易形成闭合的链路,这样在循环的时候只要有线程对这个HashMap进行get操作就会产生死循环。
  • 多线程put操作后,get操作导致死循环。
  • 多线程put非NULL元素后,get得到NULL值。
  • 多线程put操作,导致元素丢失。
  1. HashMap是非线程安全的,多线程下应该用ConcurrentHashMap,

(九)HashMap出现Hash DOS攻击的问题

通过Hash碰撞攻击json数据完成DOS攻击

(十)ConcurrentHashMap 的工作原理及代码实现,如何统计所有的元素个数

采用锁分段技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。用Segment维护着的HashEntey数组统计元素个数。

三、进程和线程

(一)线程和进程的概念、并行和并发的概念

  1. 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
  2. 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。进程内一个相对独立、可调度的执行单元,是系统独立调度和分派CPU的基本单位,也指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。(理解:在电脑上打开一个邮箱,开启了一个进程,打开一个浏览器,开启了一个进程,当我使用邮箱的时候,收邮件是一个线程,发邮件是一个线程,写邮件又是一个线程,这几个线程同时为进程邮箱工作,完成邮箱的全部功能。)
  3. 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行;并行(parallellism):指在同一时刻,有多条指令在多个处理器上同时执行。

(二)创建线程的方式及实现

  1. 继承Thread类创建线程类
    (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
    (2)创建Thread子类的实例,即创建了线程对象。
    (3)调用线程对象的start()方法来启动该线程。
  2. 通过Runnable接口创建线程类
    (1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
    (2)创建 Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
    (3)调用线程对象的start()方法来启动该线程。
  3. 通过Callable和Future创建线程
    (1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
    (2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。)
    (3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
    (4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

(三)进程间的通信方式

  1. 管道:通常指无名管道,是UNIX系统IPC最古老的形式。是半双工的(数据只能在一个方向上流动),具有固定的读端和写端。速度慢,容量有限,只能在父子进程或兄弟进程间通信。
  2. FIFO:也被成为命名管道,是一种文件类型。有路径名与之关联,以一种特殊文件形式存在于文件系统中。任何进程间都能通讯,但速度慢。
  3. 消息队列:消息的链接表,存放在内核中。一个消息队列由一个标识符(队列ID)来标识。容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题。
    (1)消息队列是面向记录的,消息具有特定的格式和特定的优先级。
    (2)消息队列独立与发送与接受进程。进程终止时,消息队列及内容不会被删除。
    (3)消息队列可以实现消息的随机查询,不一定要先进先出的次序读取,也可以按消息类型读取。
  4. 信号量:是一个计数器,用于实现进程间的互斥与同步,而不是用于存储进程间的通信数据。不能传递复杂消息,只能用来同步。
    (1)信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
    (2)信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
    (3)每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
    (4)支持信号量组。
  5. 共享内存:指两个或多个线程共享一个给定的存储区,进程是直接对内存进行存取,最快的一种IPC,因为多个进程可以同时操作,所以需要进行同步。通常和信号量结合使用,信号量用来同步对共享内存的访问。

(四)CountDownLatch、CyclicBarrier 原理和区别

CountDuwnLatchCyclicBarrier
减计数方式加计数方式
计算为0时释放所有等待的线程计数达到指定值时释放所有等待线程
计算为0时,无法重置计数达到指定值时,计数置0重新开始
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞
不可重复利用可重复利用

(五)Semaphore 原理

Semaphore是计数信号量。Semaphore管理一系列许可。每个acquire方法阻塞,直到有一个许可可以获得然后拿走一个许可;每个release方法增加一个许可,会释放一个阻塞的acquire方法。常用于限制某种资源的线程数量。

(六)Exchanger 原理

Exchanger是一个用于线程间协作的工具类。用于进行线程间的数据交换。提供一个同步点,在这个同步点两个线程可以交换彼此的数据。如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

(七)ThreadLocal 原理

  1. ThreadLocal是一个线程的局部变量,通常是类中的private、static字段,是一个线程所单独持有的,其他线程不能访问。
  2. OOM: ThreadLocal 里面使用了一个存在弱引用的 map ,map 的类型是 ThreadLocal.ThreadLocalMap。Map 中的 key 为一个 ThreadLocal 实例本身,而这个 key 使用弱引用指向 ThreadLocal 。当 ThreadLocal 实例置为 null 后,没有任何强引用指向 ThreadLocal 实例,所以ThreadLocal 将会被 GC 回收。但是我们的 value 却不能回收,而这块 value 永远不会被访问到。所以存在着内存泄漏。因为存在一条从 Current Thread 链接过来的强引用,只有当 Thread 结束以后, Current Thread 就不会存在栈中,强引用断开,Current Thread、Map Value 将全部GC回收。解决方法:每次使用完ThreadLocal,都调用它的remove()方法,清除数据。

(八)线程池的实现原理

  1. 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建),则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程;
  2. 线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储到这个工作队列里;如果工作队列满了,则进入下个流程;
  3. 判断线程池的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

(九)线程池的几种实现方式

通过ExecutorService线程池接口,地冠以了4种线程池:

  1. newCachedThreadPool:根据任务数创建线程,适用与执行很多短期异步的小程序或者负载较轻的服务器。
  2. newFixedThreadPool:固定初始化几个线程。适用于执行长期的任务,性能好。
  3. newSingleThreadExecutor:创建只有一个线程的线程池,且线程存活时间无限,当线程繁忙时,新任务会进入阻塞队列(无界的阻塞队列)。适用于一个任务一个任务执行的场景。
  4. newScheduledThreadPool:创建固定大小的线程池,线程池内线程存活时间无限,线程池可以支持定时及周期性任务执行。如果所有线程都处于繁忙状态,新任务会进入DelayedWorkQueue队列中,是一种按照超时时间排序的队列结构。适用于周期性执行任务的场景。

(十)线程的生命周期,状态是如何转移的

在这里插入图片描述
(1)New:创建线程对象后,该线程处于新建状态,此时它不能运行,仅有Java虚拟机为其分配了内存;
(2)Runnable:线程对象调用了start()方法后,该线程就进入了就绪状态(也称可运行状态)。它只具备了运行的条件,能否获得CPU的使用权开始运行,还需要等待系统的调度;
(3)Runing:处于就绪状态的线程获得了CPU使用权,执行run()方法中的线程执行体,则线程处于运行状态。当一个线程启动后,当使用完系统分配的时间后,系统就会剥脱该线程占用的CPU资源,让其他线程获得执行的机会。只有处于就绪状态的线程才可能转换到运行状态。
(4)Blocked:一个正在执行的线程在某些特殊情况下,如执行耗时的输入/输出操作时,会放弃CPU的使用权,进入阻塞状态。线程进入阻塞状态后,就不能进入排队队列。只有当引用阻塞的原因,被消除后,线程才可以进入就绪状态。
——当线程试图获取某个对象的同步锁时,如果该锁被其他线程所持有,则当前线程进入阻塞状态,如果想从阻塞状态进入就绪状态必须得获取到其他线程所持有的锁。
——当线程调用了一个阻塞式的IO方法时,该线程就会进入阻塞状态,如果想进入就绪状态就必须要等到这个阻塞的IO方法返回。
——当线程调用了某个对象的wait()方法时,也会使线程进入阻塞状态,notify()方法唤醒。
——调用了Thread的sleep(long millis)。线程睡眠时间到了会自动进入阻塞状态。
——一个线程调用了另一个线程的join()方法时,当前线程进入阻塞状态。等新加入的线程运行结束后会结束阻塞状态,进入就绪状态。
线程从阻塞状态只能进入就绪状态,而不能直接进入运行状态,即结束阻塞的线程需要重新进入可运行池中,等待系统的调度。
(5)Terminated:线程的run()方法正常执行完毕或者线程抛出一个未捕获的异常(Exception)、错误(Error),线程就进入死亡状态。一旦进入死亡状态,线程将不再拥有运行的资格,也不能转换为其他状态。

四、锁机制

(一)什么是线程安全,如何保证线程安全

  1. 线程安全:多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
  2. 保证线程安全:
    (1) 使用线程安全的类
    (2) 使用synchronized同步代码块,或者使用Lock锁
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值