Java并发-LockSupport

概述

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。

方法列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 返回提供给最近一次尚未解除阻塞的 park 方法调用的 blocker 对象,如果该调用不受阻塞,则返回 null。
static Object getBlocker(Thread t)
// 为了线程调度,禁用当前线程,除非许可可用。
static void park()
// 为了线程调度,在许可可用之前禁用当前线程。
static void park(Object blocker)
// 为了线程调度禁用当前线程,最多等待指定的等待时间,除非许可可用。
static void parkNanos(long nanos)
// 为了线程调度,在许可可用前禁用当前线程,并最多等待指定的等待时间。
static void parkNanos(Object blocker, long nanos)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(long deadline)
// 为了线程调度,在指定的时限前禁用当前线程,除非许可可用。
static void parkUntil(Object blocker, long deadline)
// 如果给定线程的许可尚不可用,则使其可用。
static void unpark(Thread thread)

原理

LockSupport中的park()unpark() 的作用分别是阻塞线程和解除阻塞线程,而且park()unpark()不会遇到Thread.suspendThread.resume所可能引发的死锁 问题。
因为park()unpark()有许可的存在,unpark()为线程提供“许可(permit)”,线程调用park()则等待“许可” ;调用 park() 的线程和另一个试图将其 unpark() 的线程之间的竞争将保持活性。

这个有点像信号量,可是这个“许可”是不能叠加的,“许可”是一次性的。

比如:线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,假设线程A再次调用park,则进入等待状态。
注意。unpark函数能够先于park调用。比方线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时。它发现已经有“许可”了。那么它会立即再继续执行。

代码示例1

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main.java.com.study.lock.lockSupport;

import java.util.concurrent.locks.LockSupport;

/**
* @author: whb
* @description: LockSupport测试类
*/
public class LockSupportDemoOne {
public static void main(String args[]) {
Thread thread = Thread.currentThread();

ThreadParkService service = new ThreadParkService(thread);
System.out.println(Thread.currentThread().getName() + " start1");
service.start();

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " center");
LockSupport.park(thread);
System.out.println("end");

}

}

class ThreadParkService extends Thread {

private Thread thread;

public ThreadParkService(Thread thread) {
this.thread = thread;
}

@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " wakup before");
LockSupport.unpark(thread);//解锁后线程2立马就可以执行了。
try {
System.out.println("解锁后休息三秒哦");
Thread.sleep(3000);

} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " wakup after");

}
}

执行结果1

1
2
3
4
5
6
main start1 
Thread-0 wakup before
解锁后休息三秒哦
main center
end
Thread-0 wakup after

结果说明1

主线程main先打印了自己的东西,主线程到了park时被阻塞了。
线程1异步执行打印了 wakup before
main center打印在“解锁后休息3s哦”说明线程1中的unpark先与park执行。
线程1执行了unpark了,这个时候主线程中的park立马被唤醒,开始执行了end。
然后线程1等待了3s后执行了wakup after。

总结1

unpark,可以在park之前执行。它们一对一,不管什么时候park只要有unpark过一次就好了。

park被阻塞后,unpark执行后park立即被唤醒。与wait与notify不同,notify唤醒wait后,还必须等待notify代码块中的代码执行完毕后wait才能执行。

代码示例2

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
44
package main.java.com.study.lock.lockSupport;

import java.util.concurrent.locks.LockSupport;

/**
* @author: whb
* @description: LockSupport测试示例
*/
public class LockSupportDemoTwo {
public static void main(String args[]) {
Thread thread = Thread.currentThread();

ThreadParkService2 service2 = new ThreadParkService2(thread);
System.out.println(Thread.currentThread().getName() + " start2");
service2.start();

System.out.println(" 漂亮的分割线---");

ThreadParkService service = new ThreadParkService(service2);
System.out.println(Thread.currentThread().getName() + " start1");
service.start();
}
}

class ThreadParkService2 extends Thread {

private Thread thread;

public ThreadParkService2(Thread thread) {
this.thread = thread;
}

@Override
public void run() {

System.out.println(Thread.currentThread().getName() + "-线程2开始park before");
//阻塞当前执行的线程,不能阻塞其他线程,看源码哦。里面阻塞时并不需要任何传参。
LockSupport.park(this);
//这里并不会阻塞thread线程,阻塞的还是当前线程。thread只是做一个标记方便被锁的对象。
//LockSupport.park(thread);
System.out.println(Thread.currentThread().getName() + "-线程2park after");

}
}

执行结果2

1
2
3
4
5
6
7
8
main start2 
漂亮的分割线—
Thread-0-线程2开始park before
main start1
Thread-1 wakup before
解锁后休息三秒哦
Thread-0-线程2park after
Thread-1 wakup after

结果说明2

其实看结果就是线程2阻塞了,线程1唤醒了线程2.就这么简单。

总结

  1. park()与park(Object obj)有什么区别?
    park()是可以不需要参数的哦,就是说加锁时,不需要指定对象。那么它是怎么加锁的呢?源码如下:
1
2
3
4
5
6
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);//记录当前线程等待的对象(阻塞对象);
UNSAFE.park(false, 0L);//阻塞当前线程;
setBlocker(t, null);//当前线程等待对象置为null。
}

可以看出来,阻塞的时候UNSAFE.park(false, 0L);根本不需要外面的入参数。

那么setBlocker是用来干吗的呢?

它是用来做监控用的,当前被阻塞的对象是那个,可以通过LockSupport中的get之类的方法来获取当前被阻塞的对象。
UNSAFE.park(false, 0L);锁住的是当前正在执行的线程。


  1. 代码示例2中线程2有一个参数,把主线程传过去了, 想用线程2来阻塞主线程。

main中:

1
2
3
Thread thread = Thread.currentThread(); 
ThreadParkService2 service2 = new ThreadParkService2(thread);

线程2中:

1
LockSupport.park(thread);

这样是不行的,前面说过park阻塞的是当前正在执行的线程,和外面传递进来的值无关。所以这里被阻塞的还是线程2。


  1. unpark需要带上参数,这样才知道解锁那个线程。
    LockSupport.park 锁住的是Thread.currentThread()当前线程。

Thread.currentThread()与this

类继承了Thread类后,可以使用this.getName();获取当前线程的名称,但是Thread.currentThread().getName()也获取线程名称。那么它们有什么区别呢?

继承了Thread类后使用this.getName()获取的当前对象的线程名称,而Thread.currentThread().getName()获取的是当前正在运行的线程的名称。

park和wait的区别

  1. unpark可以先于park执行,notify不能先于wait执行,不然就不能唤醒了。
  2. unpark需要指定唤醒的线程,而notify是随机唤醒一个wait。
  3. unpark解锁后park立即可以执行,notify解锁后wait需要等待notify所在的synchronized代码块执行完毕后wait才能继续执行。
  4. wait必须在Synchronizer里面才能有效,unpark则不用。
  5. wait是对象锁,park则是线程锁。

应用示例

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
package main.java.com.study.lock.lockSupport;

import java.util.concurrent.locks.LockSupport;

/**
* @author: whb
* @description: 阻塞原语测试
*/
public class LockSupportTest {
/**
* 主线程
*/
private static Thread mainThread;

public static void main(String[] args) {
ThreadA ta = new ThreadA("ta");
//获取主线程
mainThread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + " start ta");
//启动线程ta
ta.start();
System.out.println(Thread.currentThread().getName() + " block");
//主线程阻塞
LockSupport.park(mainThread);
System.out.println(Thread.currentThread().getName() + " continue");
}

static class ThreadA extends Thread {
public ThreadA(String name) {
super(name);
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " wakeup others.");
LockSupport.unpark(mainThread);
}
}
}

执行结果:
img

本文标题:Java并发-LockSupport

文章作者:王洪博

发布时间:2019年03月04日 - 11:03

最后更新:2019年09月23日 - 09:09

原始链接:http://whb1990.github.io/posts/2c562869.html

▄︻┻═┳一如果你喜欢这篇文章,请点击下方"打赏"按钮请我喝杯 ☕
0%