2020.9.1知识点复习
3.2 线程的基本方法有什么?
线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等。
- 线程等待(wait)
调用者线程进入waiting状态,等待另外线程通知或被中断才会返回。调用后会释放对象的锁。 - 线程睡眠(sleep)
导致当前线程休眠,调用不会释放当前占有的锁,会导致线程进入timed-wating状态。 - 线程让步(yield)
yield 会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片。 - 线程中断(interrupt)
给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。 - 等待其他线程终止(join)
在当前线程中调用一个线程的 join() 方法,则当 前线程转为阻塞状态,回到另一个线程结束,当前线程再由阻塞状态变为就绪状态。 - 线程唤醒(notify)
唤醒在此对象监视器上等待的单个线程,如果所有线 程都在此对象上等待,则会选择唤醒其中一个线程,在对象的监视器上等待,直到当前的线程放弃 此对象上的锁定,才能继续执行被唤醒的线程。
3.3 在 java 中 wait 和 sleep 方法的不同?
wait 会释放锁,而 sleep 一直持有锁。wait 通常被用于线程 间交互,sleep 通常被用于暂停执行。
3.4 同步锁与死锁
同步锁
当多个线程同时访问同一个数据时,保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享 数据。
死锁
就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.
3.5 乐观锁和悲观锁是什么?
乐观锁
每次去拿数据的时候都认为别人不会修改,所以不会上锁
悲观锁
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候 都会上锁
3.6 线程池原理
的工作主要是控制运行的线程的数量,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线 程执行完毕,再从队列中取出任务来执行。
线程复用:
Thread 的类中当调用 start时会调用该类的 run 方法。如果就是调用了Runnable对象的run()我们可以继承重写 Thread 类,在其 start 方法中添加不断循环调用传递过来的 Runnable 对象
线程池的组成:
- 线程池管理器:用于创建并管理线程池
- 工作线程:线程池中的线程
- 任务接口:每个任务必须实现的接口,用于工作线程调度其运行
- 任务队列:用于存放待处理的任务,提供一种缓冲机制
拒绝策略: - AbortPolicy : 直接抛出异常,阻止系统正常运行。
- CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中.
- DiscardOldestPolicy :丢弃最老的一个请求,也就是即将被执行的一个任务务,并尝试再次提交当前任务。
- DiscardPolicy : 该策略默默地丢弃无法处理的任务,不予任何处理。
- Java 线程池工作过程:
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。
- 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是 要创建非核心线程立刻运行这个任务;
d) 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程 池会抛出异常 RejectExecutionException。 - 当一个线程完成任务时,它会从队列中取下一个任务来执行。
- 当一个线程无事可做,超过一定的时间时,线程池会判断,如果当前运行的线程数大于 corePoolSize,这个线程就被停掉。线程池的所有任务完成后, 它会收缩到 corePoolSize 的大小。
3.7 线程执行的顺序
- 当线程数小于核心线程数时,会一直创建线程直到线程数等于核心线程数;
- 当线程数等于核心线程数时,新加入的任务会被放到任务队列等待执行;
- 当任务队列已满,又有新的任务时,会创建线程直到线程数量等于最大线程数;
- 当线程数等于最大线程数,且任务队列已满时,新加入任务会被拒绝。
3.8 线程池的核心参数有哪些?
默认参数: corePoolSize = 1
(核心线程数)
即使没有任务执行也会一直存在
queueCapacity = Integer.MAX_VALUE
(任务队列容量)
当核心线程都在运行,再有任务进来,会进入任务队列排队等待
maxPoolSize = Integer.MAX_VALUE
(最大线程数)
(1) 线程池里允许存在的最大线程数量;
(2) 当任务队列已满,且数量大于等于核心线程数时,会创建新的线程;
keepAliveTime = 60 秒
(线程空闲时间)
(1) 当线程空闲时间达到 keepAliveTime 时,线程会退出
(2) 如果设置了 allowCoreThreadTimeout=true,则线程会退出直到线程数等于零
allowCoreThreadTimeout = false (允许核心线程超时)
rejectedExecutionHandler = AbortPolicy()
(任务拒绝处理器)
当线程数量达到最大线程数,且任务队列已满时,会拒绝任务
调用线程池 shutdown()方法后,会等待执行完线程池的任务之后,再 shutdown()。如 果在调用了 shutdown()方法和线程池真正 shutdown()之间提交任务,会拒绝新任务。
4.1 JVM 内存管理
• PC 寄存器(程序计数器):用于记录当前线程运行时的位置
• java 虚拟机栈:在创建线程时创建的,用来存储栈帧
• java 堆:java 堆被所有线程共享,堆的主要作用就是存储对象。
• 方法区:方发区被各个线程共享,用于存储静态变量
本地方法栈:本地方法栈的主要作用就是支持 native 方法,
4.2 GC 分代收集算法和分区收集算法区别?
- 分代收集算法 当前主流 VM 垃圾收集都采用”分代收集”(Generational Collection)算法, 这种算法会根 据对象存活周期的不同将内存划分为几块
在新生代-复制算法 每次垃圾收集都能发现大批对象已死, 只有少量存活
在老年代-标记整理算法 因为对象存活率高、没有额外空间对它进行分配担保, 就必须采用“标记—清理”或“标 记 —整理”算法来进行回收 - 分区收集算法 分区算法则将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收。
4.3 GC 垃圾收集器
Java 堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算 法; 年老代主要使用标记-整理垃圾回收算法
常见的垃圾回收器:Serial 垃圾收集器,ParNew 垃圾收集器,Parallel Scavenge 收集器等
最常用的是 G1(Garbage First):