一、线程的状态
如图上图所示,线程存在六种状态
NEW(新建)
创建后尚未启动
RUNNABLE(就绪)
可以被运行,具体有没有运行要看底层操作系统的资源调度
BLOCKED(阻塞)
请求获取 monitor lock 从而进入 synchronized 函数或者代码块,但是其它线程已经占用了该 monitor lock,所以出于阻塞状态。要结束该状态进入从而 RUNABLE 需要其他线程释放 monitor lock。
WAITING(无限期等待)
不见不散
等待其他线程显式唤醒
阻塞和等待的区别在于,阻塞是被动的,它是在等待获取 monitor lock。而等待是主动的,通过调用 Object.wait() 等方法进入。
进入方法 | 退出方法 |
---|---|
没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() |
没有设置 Timeout 参数的 Thread.join() 方法 | 被调用的线程执行完毕 |
LockSupport.park() 方法 | LockSupport.unpark(Thread) |
TIMED_WAITING(限期等待)
过时不候
无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。
进入方法 | 退出方法 |
---|---|
Thread.sleep() 方法 | 时间结束 |
设置了 Timeout 参数的 Object.wait() 方法 | 时间结束 / Object.notify() / Object.notifyAll() |
设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 |
LockSupport.parkNanos() 方法 | LockSupport.unpark(Thread) |
LockSupport.parkUntil() 方法 | LockSupport.unpark(Thread) |
TERMINATED(死亡)
可以是线程结束任务之后自己结束,或者产生了异常而结束。
二、互斥同步
1.Synchronized
1.同步一个代码块
public void func() { synchronized (this) { // ... } }
锁的this只作用在同一个对象上,如果调用两个及以上对象的同步代码块就不会同步。
2.同步一个方法
public synchronized void func () { // ... }
它和同步代码块一样,作用于同一个对象。
3.同步一个类
public void func() { synchronized (SynchronizedExample.class) { // ... } }
作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步。
public class SynchronizedExample { public void func2() { synchronized (SynchronizedExample.class) { for (int i = 0; i <= 10; i++) { System.out.print(i); } } } }
public static void main(String[] args) { SynchronizedExample e1 = new SynchronizedExample(); SynchronizedExample e2 = new SynchronizedExample(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> e1.func2()); executorService.execute(() -> e2.func2()); }
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
4.同步一个静态方法
public synchronized static void fun() { // ... }
作用于整个类,和同步一个类是一样的
2.ReentrantLock
这里要涉及到AQS
三、并发编程中的三个概念
可见性、原子性、有序性
1.有序性演示
jcstress是java并发压测工具。
修改pom文件,添加依赖:
<dependencies> <dependency> <groupId>org.openjdk.jcstress</groupId> <artifactId>jcstress-core</artifactId> <version>0.5</version> </dependency> <dependency> <groupId>org.openjdk.jcstress</groupId> <artifactId>jcstress-samples</artifactId> <version>0.5</version> </dependency> </dependencies>
@JCStressTest @Outcome(id = {"1", "4"}, expect = Expect.ACCEPTABLE, desc = "ok") @Outcome(id = "0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "danger") @State public class T03Ordering { int num = 0; boolean ready = false; @Actor public void actor1(I_Result r) { if (ready) { r.r1 = num + num; } else { r.r1 = 1; } } @Actor public void actor2(I_Result r) { num = 2; ready = true; } }
//压测结果显示 Observed state Occurrences Expectation Interpretation 0 560 ACCEPTABLE_INTERESTING danger 1 74,567,949 ACCEPTABLE ok 4 24,642,982 ACCEPTABLE ok
程序代码在执行过程中的先后顺序,由于Java在编译期以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码时的顺序。
可重入锁
遇到异常 释放锁
synchronized实现
重量级
锁升级的概念
无锁
对象头记录线程id 偏向锁
线程征用 自旋锁
10次以后
升级为重量级锁
线程的概念、启动方式、常用方法