线程专题

线程专题笔记

##线程的状态转换

这是一个线程状态图

线程有

  • ready to run
  • Running
  • Dead
  • sleeping
  • waiting
  • Blocked

这 6 个状态

另一种说法的5个状态

  • 初始状态

    1
    实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态
  • 可运行状态

    1
    2
    3
    4
    5
    1.可运行状态只是说你资格运行,调度程序没有挑选到你,你就永远是可运行状态。
    2.调用线程的start()方法,此线程进入可运行状态。
    3.当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态。
    4.当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入可运行状态。
    5.锁池里的线程拿到对象锁后,进入可运行状态。
  • 运行状态

    1
    线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
  • 死亡状态

    1
    2
    3
    1. 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它死去。
    这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。
    2. 在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
  • 阻塞状态

    1
    2
    3
    1.当前线程T调用Thread.sleep()方法,当前线程进入阻塞状态。
    2.运行在当前线程里的其它线程t2调用join()方法,当前线程进入阻塞状态。
    3.等待用户输入的时候,当前线程进入阻塞状态。


首先 从start 开始

  1. 一个正常的线程周期为
    start --> ready to run --> run --> Dead

  2. 如果不幸遇到 sleep 那么会在sleep时间结束后 进入run状态
    start --> read to run --> run --> sleep --> run-->Dead

  3. 如果不幸遇到wait 那么需要在被notify后 再进入 run状态
    start --> ready to run -->run --> wait -->(Object.notify() or object.notifyall()) --> run --> dead

  4. 如果不幸遇到 blocked for io (类似读取数据库,做文件读取工作) OR enter a synchronize code (其他线程进入同步代码块了)
    那么 会在 锁被自己获取到 或者 io中数据读取到了 的时候 再进入running状态
    start --> ready to run -->run --> blocked -->(received date or get the lock) --> run --> dead

锁专题

synchronized 和 lock的区别

  • synchronized是一个Java的关键字,Lock是一个接口;
  • synchronized代码块执行完或线程抛出异常时结束线程,Lock必须显示调用释放锁的方法:unlock();
  • synchronized修饰的锁其他线程在等待获取锁的阶段,会一直阻塞等待直到得到锁为止(不可中断锁);Lock有多种方式可以获取锁,不一定一直阻塞等待(可中断锁)。
  • synchronized无法判断锁的状态,Lock可以判断;
  • synchronized是非公平锁,而Lock可以设置为公平锁;

Join操作

join 让父线程等待子线程结束之后才能继续运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  public class ThreadJoinStudy {

public void a(Thread joinThread){
System.out.println("function a run begin");
try {
joinThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("function a run end");
}

public void b(){
System.out.println("function b run begin");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("function b run end");
}

public static void main(String[] args) {
ThreadJoinStudy threadJoinStudy = new ThreadJoinStudy();

Thread thread = new Thread(()->{
threadJoinStudy.b();
});

new Thread(()->{
threadJoinStudy.a(thread);
}).start();

thread.start();
}
}

输出结果为

function a run begin
function b run begin
function b run end
function a run end

StampedLock

在老的JDK中 一有 ReentrantReadWriteLock
read read 不互斥
read write 互斥
write write 互斥

这种是悲观锁

在ReentrantReadWriteLock 使用中 由于大多为读操作 会导致写线程处于饥饿状态(即一直抢占不到CPU)
为了解决这种问题
在 StampedLock中
read操作 并不会排斥 write 操作
会这样 如果 read 和 write 操作冲突 ,那么会 read 和 write 一起进行,等write操作执行完成后,再次执行一把read操作