Java多线程实践总结

分类: JVM 发布于:

Multiple thread concept application.

参考

https://en.wikipedia.org/wiki/Thread_safety

https://www.uml-diagrams.org/java-thread-uml-state-machine-diagram-example.html

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html

线程安全的定义

  1. 它是相对于多线程环境而言的,如果是单线程环境谈不上线程安全。

  2. 它谈论的目标是线程间的共享变量。

多个线程可以同时读写的变量。 这常常是人为引入的复杂。

  1. 如果代码的执行是竞争条件无关的(不包含线程间共享变量),则 代码执行过程是线程安全的

  2. 执行的代码包含线程间的共享变量,但是变量被竞争条件保护,这种情况成为条件安全

实现线程安全的几种方式

1) 执行程序可重入

什么是可重入?

从结果上看,不管多少线程同时执行,结构都相同。 从集合论上讲,这是一个一对一的函数。

通常的实现方法是函数调用的依赖全部保存在栈上

为什么保存在栈上可以做到可重入?这是栈的性质决定的。

2) 使用本地化的线程变量。

其它线程无法看到当前变量。

3) 使用不可变的对象(immutable objects)

对象的状态在完成构造以后就不会被修改。

4) 排它执行

使用锁机制保证同时只有一个线程执行临界区内的代码。

5) 原子操作

Java 中线程的状态转换

线程状态转换

1) 以下是线程各状态的含义

  • A. New 状态

线程实例化后,还没有执行start方法的状态。

  • B. Runnable

线程执行start方法后,但是还没有没thread scheduler 选中。

  • C. waiting

这个状态的线程的核心职能是等待被通知,然后进入blocked状态。

线程等待另外一个线程完成执行的动作时的状态。

以下三个方法可以让一个线程处于waiting状态

  • object的wait方法(没有timeout参数)

  • Thead的join方法

  • Locksupport的park方法。

如果一个对象让一个线程处于waiting状态, 这个线程会一直(忠诚地)等待,直到被notify。

  • D. blocked

这个状态的对象的核心职责是 等待锁释放

阻塞状态, 它在等待区(entry set 或者 wait set)等待锁的释放。 如果一个方法锁被线程占用,另外一个线程就会进入Blocked状态。

  • E. timed_waiting

这个状态的核心职能是等待到达指定时间,然后进入runnable状态或者blocked状态。

线程等待另外一个线程到达指定的时间时的状态。

  • terminated

线程终结后的状态。

与线程有关的常用方法

1) sleep 方法

  • 静态方法,使用当前线程(调用方法的线程)暂停一段时间, 让其他线程有机会继续执行。

这个方法需要捕捉被中断异常 InterruptedException。当线程sleep时被中断会立刻抛出InterruptedException异常。

  • sleep 时间结束后会重新回到Runnable

  • 线程sleep后会进入timed_wait状态,在此期间不释放它占用的对象锁。也就是说,其它线程仍然没有机会执行它占有的资源。

2) wait 方法

  • 这个方法是对象方法,任何java对象都包含这个方法,与notifiy是一对。

  • 必须在synchronized方法区内调用。 原因是线程等待的对象被notify以后,线程需要重新进入执行状态。

3) sleep 和 wait方法的区别

  • sleep() 是Thread类的静态方法;wait方法是Ojbect类的方法

  • sleep()进入timed_waiting状态以后,仍然占有对象锁,wait()会释放对象锁。

  • sleep() 的状态转换是timed_waiting到Runnable, wait()被notify以后下一个状态是Blocked,它需要重新等待锁释放。

  • wait()必须被包含在synchronized()方法内

4) sleep与yield方法的区别

  • yield 不设定超时时间, sleep包含一个事件参数。

  • 线程sleep以后,任意优先级的对象都可被调度。 yield方法让出执行权以后,只调度相同优先级的线程。

monitor 和 lock 的区别

lock(锁)是JDK对象(object)实现中的一个字段,是对象属性,在运行时中随对象保存在堆中。运行过程中,这个字段会被线程检查。

任何线程在访问一个对象之前,线程必须设置或者拥有这个对象的lock,当其他线程尝试访问对象实例的时候,它必须等待当前对象的锁处于释放状态。

一个对象要让一个线程拥有它的锁,对象通常有两种实现方式:

  • 使用synchronized关键字。

  • 调用对象的wait和notify方法。

下面是monitor部分

monitor是锁对应的的实现。

monitor由jvm编译后字节码中的一对指令(monitorenter/monitorexit)实现。

它的设计目的有2个:

  • 保护共享数据。

  • 完成线程之间的协作(cooperate. 比如 消费者线程需要等待生产者的状态。