- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java多线程基础——Lock类由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
之前已经说道,JVM提供了synchronized关键字来实现对变量的同步访问以及用wait和notify来实现线程间通信。在jdk1.5以后,JAVA提供了Lock类来实现和synchronized一样的功能,并且还提供了Condition来显示线程间通信.
Lock类是Java类来提供的功能,丰富的api使得Lock类的同步功能比synchronized的同步更强大。本文章的所有代码均在Lock类例子的代码 。
本文主要介绍一下内容:
Lock类 。
Lock类实际上是一个接口,我们在实例化的时候实际上是实例化实现了该接口的类Lock lock = new ReentrantLock();。用synchronized的时候,synchronized可以修饰方法,或者对一段代码块进行同步处理.
前面讲过,针对需要同步处理的代码设置对象监视器,比整个方法用synchronized修饰要好。Lock类的用法也是这样,通过Lock对象lock,用lock.lock来加锁,用lock.unlock来释放锁。在两者中间放置需要同步处理的代码.
具体的例子如下:
1
2
3
4
5
6
7
8
9
10
|
public
class
MyConditionService {
private
Lock lock =
new
ReentrantLock();
public
void
testMethod(){
lock.lock();
for
(
int
i =
0
;i <
5
;i++){
System.out.println(
"ThreadName = "
+ Thread.currentThread().getName() + (
" "
+ (i +
1
)));
}
lock.unlock();
}
}
|
测试的代码如下:
1
2
3
4
5
6
7
8
|
MyConditionService service =
new
MyConditionService();
new
Thread(service::testMethod).start();
new
Thread(service::testMethod).start();
new
Thread(service::testMethod).start();
new
Thread(service::testMethod).start();
new
Thread(service::testMethod).start();
Thread.sleep(
1000
*
5
);
|
结果太长就不放出来,具体可以看我源码。总之,就是每个线程的打印1-5都是同步进行,顺序没有乱.
通过下面的例子,可以看出Lock对象加锁的时候也是一个对象锁,持续对象监视器的线程才能执行同步代码,其他线程只能等待该线程释放对象监视器.
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
|
public
class
MyConditionMoreService {
private
Lock lock =
new
ReentrantLock();
public
void
methodA(){
try
{
lock.lock();
System.out.println(
"methodA begin ThreadName="
+ Thread.currentThread().getName() +
" time="
+ System.currentTimeMillis());
Thread.sleep(
1000
*
5
);
System.out.println(
"methodA end ThreadName="
+ Thread.currentThread().getName() +
" time="
+ System.currentTimeMillis());
}
catch
(Exception e){
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
public
void
methodB(){
try
{
lock.lock();
System.out.println(
"methodB begin ThreadName="
+ Thread.currentThread().getName() +
" time="
+ System.currentTimeMillis());
Thread.sleep(
1000
*
5
);
System.out.println(
"methodB end ThreadName="
+ Thread.currentThread().getName() +
" time="
+ System.currentTimeMillis());
}
catch
(Exception e){
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
}
|
测试代码如下:
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
|
public
void
testMethod()
throws
Exception {
MyConditionMoreService service =
new
MyConditionMoreService();
ThreadA a =
new
ThreadA(service);
a.setName(
"A"
);
a.start();
ThreadA aa =
new
ThreadA(service);
aa.setName(
"AA"
);
aa.start();
ThreadB b =
new
ThreadB(service);
b.setName(
"B"
);
b.start();
ThreadB bb =
new
ThreadB(service);
bb.setName(
"BB"
);
bb.start();
Thread.sleep(
1000
*
30
);
}
public
class
ThreadA
extends
Thread{
private
MyConditionMoreService service;
public
ThreadA(MyConditionMoreService service){
this
.service = service;
}
@Override
public
void
run() {
service.methodA();
}
}
public
class
ThreadB
extends
Thread{
private
MyConditionMoreService service;
public
ThreadB(MyConditionMoreService service){
this
.service = service;
}
@Override
public
void
run() {
super
.run();
service.methodB();
}
}
|
结果如下:
1
2
3
4
5
6
7
8
|
methodA begin ThreadName=A time=1485590913520
methodA end ThreadName=A time=1485590918522
methodA begin ThreadName=AA time=1485590918522
methodA end ThreadName=AA time=1485590923525
methodB begin ThreadName=B time=1485590923525
methodB end ThreadName=B time=1485590928528
methodB begin ThreadName=BB time=1485590928529
methodB end ThreadName=BB time=1485590933533
|
可以看出Lock类加锁确实是对象锁。针对同一个lock对象执行的lock.lock是获得对象监视器的线程才能执行同步代码 其他线程都要等待.
在这个例子中,加锁,和释放锁都是在try-finally。这样的好处是在任何异常发生的情况下,都能保障锁的释放.
Lock类其他的功能 。
如果Lock类只有lock和unlock方法也太简单了,Lock类提供了丰富的加锁的方法和对加锁的情况判断。主要有 。
实现公平锁 。
在实例化锁对象的时候,构造方法有2个,一个是无参构造方法,一个是传入一个boolean变量的构造方法。当传入值为true的时候,该锁为公平锁。默认不传参数是非公平锁.
公平锁:按照线程加锁的顺序来获取锁 。
非公平锁:随机竞争来得到锁 。
此外,JAVA还提供isFair()来判断一个锁是不是公平锁.
获取当前线程锁定的个数 。
Java提供了getHoldCount()方法来获取当前线程的锁定个数。所谓锁定个数就是当前线程调用lock方法的次数。一般一个方法只会调用一个lock方法,但是有可能在同步代码中还有调用了别的方法,那个方法内部有同步代码。这样,getHoldCount()返回值就是大于1.
下面的方法用来判断等待锁的情况 。
获取等待锁的线程数 。
Java提供了getQueueLength()方法来得到等待锁释放的线程的个数.
查询指定的线程是否等待获取此锁定 。
Java提供了hasQueuedThread(Thread thread)查询该Thread是否等待该lock对象的释放.
查询是否有线程等待获取此锁定 。
同样,Java提供了一个简单判断是否有线程在等待锁释放即hasQueuedThreads().
下面的方法用来判断持有锁的情况 。
查询当前线程是否持有锁定 。
Java不仅提供了判断是否有线程在等待锁释放的方法,还提供了是否当前线程持有锁即isHeldByCurrentThread(),判断当前线程是否有此锁定.
判断一个锁是不是被线程持有 。
同样,Java提供了简单判断一个锁是不是被一个线程持有,即isLocked() 。
下面的方法用来实现多种方式加锁 。
加锁时如果中断则不加锁,进入异常处理 。
Lock类提供了多种选择的加锁方法,lockInterruptibly()也可以实现加锁,但是当线程被中断的时候,就会加锁失败,进行异常处理阶段。一般这种情况出现在该线程已经被打上interrupted的标记了.
尝试加锁,如果该锁未被其他线程持有的情况下成功 。
Java提供了tryLock()方法来进行尝试加锁,只有该锁未被其他线程持有的基础上,才会成功加锁.
上面介绍了Lock类来实现代码的同步处理,下面介绍Condition类来实现wait/notify机制.
Condition类 。
Condition是Java提供了来实现等待/通知的类,Condition类还提供比wait/notify更丰富的功能,Condition对象是由lock对象所创建的。但是同一个锁可以创建多个Condition的对象,即创建多个对象监视器。这样的好处就是可以指定唤醒线程。notify唤醒的线程是随机唤醒一个.
下面,看一个例子,显示简单的等待/通知 。
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
|
public
class
ConditionWaitNotifyService {
private
Lock lock =
new
ReentrantLock();
public
Condition condition = lock.newCondition();
public
void
await(){
try
{
lock.lock();
System.out.println(
"await的时间为 "
+ System.currentTimeMillis());
condition.await();
System.out.println(
"await结束的时间"
+ System.currentTimeMillis());
}
catch
(Exception e){
e.printStackTrace();
}
finally
{
lock.unlock();
}
}
public
void
signal(){
try
{
lock.lock();
System.out.println(
"sign的时间为"
+ System.currentTimeMillis());
condition.signal();
}
finally
{
lock.unlock();
}
}
}
|
测试的代码如下:
1
2
3
4
5
|
ConditionWaitNotifyService service =
new
ConditionWaitNotifyService();
new
Thread(service::await).start();
Thread.sleep(
1000
*
3
);
service.signal();
Thread.sleep(
1000
);
|
结果如下:
1
2
3
|
await的时间为 1485610107421
sign的时间为1485610110423
await结束的时间1485610110423
|
condition对象通过lock.newCondition()来创建,用condition.await()来实现让线程等待,是线程进入阻塞。用condition.signal()来实现唤醒线程。唤醒的线程是用同一个conditon对象调用await()方法而进入阻塞。并且和wait/notify一样,await()和signal()也是在同步代码区内执行.
此外看出await结束的语句是在获取通知之后才执行,确实实现了wait/notify的功能。下面这个例子是展示唤醒制定的线程.
1
2
3
4
5
6
7
8
9
10
|
ConditionAllService service =
new
ConditionAllService();
Thread a =
new
Thread(service::awaitA);
a.setName(
"A"
);
a.start();
Thread b =
new
Thread(service::awaitB);
b.setName(
"B"
);
b.start();
Thread.sleep(
1000
*
3
);
service.signAAll();
Thread.sleep(
1000
*
4
);
|
结果如下:
1
2
3
4
|
begin awaitA时间为 1485611065974ThreadName=A
begin awaitB时间为 1485611065975ThreadName=B
signAll的时间为1485611068979ThreadName=main
end awaitA时间为1485611068979ThreadName=A
|
该结果确实展示用同一个condition对象来实现等待通知.
对于等待/通知机制,简化而言,就是等待一个条件,当条件不满足时,就进入等待,等条件满足时,就通知等待的线程开始执行。为了实现这种功能,需要进行wait的代码部分与需要进行通知的代码部分必须放在同一个对象监视器里面。执行才能实现多个阻塞的线程同步执行代码,等待与通知的线程也是同步进行。对于wait/notify而言,对象监视器与等待条件结合在一起 即synchronized(对象)利用该对象去调用wait以及notify。但是对于Condition类,是对象监视器与条件分开,Lock类来实现对象监视器,condition对象来负责条件,去调用await以及signal.
Condition类的其他功能 。
和wait类提供了一个最长等待时间,awaitUntil(Date deadline)在到达指定时间之后,线程会自动唤醒。但是无论是await或者awaitUntil,当线程中断时,进行阻塞的线程会产生中断异常。Java提供了一个awaitUninterruptibly的方法,使即使线程中断时,进行阻塞的线程也不会产生中断异常.
读写锁 。
Lock类除了提供了ReentrantLock的锁以外,还提供了ReentrantReadWriteLock的锁。读写锁分成两个锁,一个锁是读锁,一个锁是写锁。读锁与读锁之间是共享的,读锁与写锁之间是互斥的,写锁与写锁之间也是互斥的.
看下面的读读共享的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
class
ReadReadService {
private
ReentrantReadWriteLock lock =
new
ReentrantReadWriteLock();
public
void
read(){
try
{
try
{
lock.readLock().lock();
System.out.println(
"获得读锁"
+ Thread.currentThread().getName() +
" "
+ System.currentTimeMillis());
Thread.sleep(
1000
*
10
);
}
finally
{
lock.readLock().unlock();
}
}
catch
(InterruptedException e){
e.printStackTrace();
}
}
}
|
测试的代码和结果如下:
1
2
3
4
5
6
7
|
ReadReadService service =
new
ReadReadService();
Thread a =
new
Thread(service::read);
a.setName(
"A"
);
Thread b =
new
Thread(service::read);
b.setName(
"B"
);
a.start();
b.start();
|
结果如下:
1
2
|
获得读锁A 1485614976979
获得读锁B 1485614976981
|
两个线程几乎同时执行同步代码.
下面的例子是写写互斥的例子 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
class
WriteWriteService {
private
ReentrantReadWriteLock lock =
new
ReentrantReadWriteLock();
public
void
write(){
try
{
try
{
lock.writeLock().lock();
System.out.println(
"获得写锁"
+ Thread.currentThread().getName() +
" "
+System.currentTimeMillis());
Thread.sleep(
1000
*
10
);
}
finally
{
lock.writeLock().unlock();
}
}
catch
(InterruptedException e){
e.printStackTrace();
}
}
}
|
测试代码和结果如下:
1
2
3
4
5
6
7
8
|
WriteWriteService service =
new
WriteWriteService();
Thread a =
new
Thread(service::write);
a.setName(
"A"
);
Thread b =
new
Thread(service::write);
b.setName(
"B"
);
a.start();
b.start();
Thread.sleep(
1000
*
30
);
|
结果如下:
1
2
|
获得写锁A 1485615316519
获得写锁B 1485615326524
|
两个线程同步执行代码 。
读写互斥的例子:
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
|
public
class
WriteReadService {
private
ReentrantReadWriteLock lock =
new
ReentrantReadWriteLock();
public
void
read(){
try
{
try
{
lock.readLock().lock();
System.out.println(
"获得读锁"
+ Thread.currentThread().getName()
+
" "
+ System.currentTimeMillis());
Thread.sleep(
1000
*
10
);
}
finally
{
lock.readLock().unlock();
}
}
catch
(InterruptedException e){
e.printStackTrace();
}
}
public
void
write(){
try
{
try
{
lock.writeLock().lock();
System.out.println(
"获得写锁"
+ Thread.currentThread().getName()
+
" "
+ System.currentTimeMillis());
Thread.sleep(
1000
*
10
);
}
finally
{
lock.writeLock().unlock();
}
}
catch
(InterruptedException e){
e.printStackTrace();
}
}
}
|
测试的代码如下:
1
2
3
4
5
6
7
8
9
|
WriteReadService service =
new
WriteReadService();
Thread a =
new
Thread(service::write);
a.setName(
"A"
);
a.start();
Thread.sleep(
1000
);
Thread b =
new
Thread(service::read);
b.setName(
"B"
);
b.start();
Thread.sleep(
1000
*
30
);
|
结果如下:
1
2
|
获得写锁A 1485615633790
获得读锁B 1485615643792
|
两个线程读写之间也是同步执行代码.
总结 。
本文介绍了新的同步代码的方式Lock类以及新的等待/通知机制的实现Condition类。本文只是很简单的介绍了他们的概念和使用的方式。关于Condition以及读写锁还有更多的内容,将放在以后的博客中.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我! 。
原文链接:http://www.cnblogs.com/qifengshi/p/6354890.html 。
最后此篇关于Java多线程基础——Lock类的文章就讲到这里了,如果你想了解更多关于Java多线程基础——Lock类的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
vue3 快速入门系列 - 基础 前面我们已经用 vue2 和 react 做过开发了。 从 vue2 升级到 vue3 成本较大,特别是较大的项目。所以许多公司对旧项目继续使用vue2,新项目则
C# 基础 C#项目创建 这里注意win10虚拟机需要更新下补丁,不然直接下载visual studio 2022会显示版本不支持 HelloWorld C#的类文件都是以.cs结尾,入口方法为sta
关于 iPhone 内存管理的非常基本的问题: 假设我有一个 viewController,其中有几个 subview 也由 viewController 控制。当我删除顶部 viewControll
我仍在努力适应指针。不是概念——我理解内存位置、匹配可变长度的指针增量等——这是语法。这是一个我认为是我感到困惑/无法直观把握的原因之一: int a = 42; 在一个int大小的内存空间中分配并放
1. 简介 Kafka(Apache Kafka) 是一种分布式流数据平台,最初由LinkedIn开发,并于后来捐赠给Apache软件基金会,成为了一个Apache顶级项目。它被设计用于处理大规
1.想要在命令提示符下操作mysql服务器,添加系统变量。(计算机-系统属性——环境变量——path) 2.查询数据表中的数据; select selection_lis
MySQL表的增删改查(基础) 1. CRUD 注释:在SQL中可以使用“–空格+描述”来表示注释说明 CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、删除(Dele
我有一个网页,可以在加载时打开显示模式,在这个模式中,我有一个可以打开第二个模式的链接。当第二个模式关闭时(通过单击关闭按钮或单击模式外部),我想重新打开第一个模式。 对于关闭按钮,我可以通过向具有
使用 Core Data Fetched Properties,我如何执行这个简单的请求: 我希望获取的属性 ( myFetchProp ) 存储 StoreA ,它应该这样做: [myFetchPr
关闭。这个问题是opinion-based .它目前不接受答案。 想改进这个问题?更新问题,以便 editing this post 可以用事实和引用来回答它. 8年前关闭。 Improve this
最近,我得到了一个现有的Drupal项目,并被要求改进前端(HTML,JavaScript,CSS)。我在Django,PHP,Ruby等方面具有大量的前端和后端开发经验,但是我没有任何Drupal经
我试图让我的用户通过使用扫描仪类来决定要做什么,但我有一个问题,代码一旦运行就不会激活,并且它不会让我跳过任何行。我的代码如下所示: Scanner input = new Scanner(S
对模糊的标题表示歉意,因为我想不出这个名字是什么。 基本上创建一个计算学生财务付款的小程序。当我运行它时,它计算对象限额没有问题。然而,无论我尝试什么,对象“助学金”似乎除了 0 之外什么也没有提出。
这是我的代码 - main() { double x; double y = pow(((1/3 + sin(x/2))(pow(x, 3) + 3)), 1/3); prin
如果我的术语在这个问题上有误,我们深表歉意。 采取以下功能: i = 1; v = i * 2; for (j = 0; j < 4; j++ ) { console.log(v);
我的应用程序中有不同的类文件。我有 5 个类,其中 2 个是 Activity ,1 个是运行的服务。其他 2 个只是类。这两个类中变量的生命周期是多少。我知道一个 Activity 可以被操作系统杀
例如,一个方法返回一个 List 类型的对象。 public List bojangles () ... 一些代码调用方法FooBar.bojangles.iterator(); 我是 Java 的新
我遇到了一个奇怪的问题,网格的大小不适合我的屏幕。当我使用 12 列大时,它只占据屏幕的 1/3 的中间,请参见图像。我不确定是什么导致了这个问题。我没有任何会导致这种情况发生的奇怪 CSS。我不会在
我尝试使用头文件和源文件,但遇到了问题。因此,我对我正在尝试做的事情做了一个简化版本,我在 CodeBlocks 中遇到了同样的错误(undefined reference to add(double
我正在为我的网格系统使用基础,但这在任何网格系统中都可能是一个问题。我基本上用一个容器包裹了 3 个单元格,但其中一个单元格应该长到页面边框(留在我的 Sampe-Image 中)但这也可能在右侧)。
我是一名优秀的程序员,十分优秀!