Java中,实现线程同步手段有很多,加锁是其中常用的一种,synchronized关键字是最常用的手段之一。而ReentrantLock则可以完全替代其作用,并且可以更灵活的使用锁机制,但同时,对编程水平的要求也稍微高一点,由于编程水平原因,出现bug概率提高。
总而言之,API文档这么描述ReentrantLock:一个可重入的互斥锁 Lock
,它具有与使用 synchronized
方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
两者的区别主要如下:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
示例如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentrantLock02
{
private Lock lock = new ReentrantLock();
private int num = 0 ;
public void count()
{
lock.lock();
try
{
for (int i = 0 ; i < 100000 ; i++)
{
num++;
System.out.println(num);
}
}
finally
{
lock.unlock();
}
}
public static void main(String[] args)
{
TestReentrantLock02 a = new TestReentrantLock02();
new Thread(a::count).start();
new Thread(a::count).start();
new Thread(a::count).start();
}
}
2:synchronized是非公平锁,而ReentrantLock可以构造成公平锁的形式。
public ReentrantLock(boolean fair)
创建一个具有给定公平策略的 ReentrantLock。
参数:
fair - 如果此锁应该使用公平的排序策略,则该参数为 true
示例如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestFairLock
{
private Lock unfairLock = new ReentrantLock();
private Lock fairLock = new ReentrantLock(true);
private int num = 0 ;
public void unfairAdd()
{
for (int i = 0 ; i < 100 ; i++ )
{
unfairLock.lock();
try
{
num++;
System.out.println("线程" + Thread.currentThread().getName() + "将num加到了" + num);
}
finally
{
unfairLock.unlock();
}
}
}
public void fairAdd()
{
for (int i = 0 ; i < 200 ; i++ )
{
fairLock.lock();
try
{
num++;
System.out.println("线程" + Thread.currentThread().getName() + "将num加到了" + num);
}
finally
{
fairLock.unlock();
}
}
}
public synchronized void add()
{
for (int i = 0 ; i < 300 ; i++ )
{
num++;
System.out.println("线程" + Thread.currentThread().getName() + "将num加到了" + num);
}
}
public static void main(String[] args) throws InterruptedException
{
TestFairLock test = new TestFairLock();
// 使用synchronized
// new Thread(test::add,"t1").start();
// new Thread(test::add,"t2").start();
// 使用非公平锁
// new Thread(test::unfairAdd,"t1").start();
// new Thread(test::unfairAdd,"t2").start();
// 使用公平锁
new Thread(test::fairAdd,"t1").start();
new Thread(test::fairAdd,"t2").start();
}
}
执行结果片段如下
使用公平锁:前几个没有交替执行的原因是因为两个线程没有同时启动的原因。
线程t1将num加到了1
线程t1将num加到了2
线程t1将num加到了3
线程t1将num加到了4
线程t1将num加到了5
线程t1将num加到了6
线程t1将num加到了7
线程t1将num加到了8
线程t1将num加到了9
线程t1将num加到了10
线程t1将num加到了11
线程t1将num加到了12
线程t1将num加到了13
线程t2将num加到了14
线程t1将num加到了15
线程t2将num加到了16
线程t1将num加到了17
线程t2将num加到了18
线程t1将num加到了19
线程t2将num加到了20 ......
使用cynchronized和ReentrantLock的非公平策略结果一致如下:由于计算量小,线程一次执行机会就执行了所有的代码。
线程t1将num加到了1
线程t1将num加到了2
线程t1将num加到了3
线程t1将num加到了4
线程t1将num加到了5
线程t1将num加到了6
线程t1将num加到了7
线程t1将num加到了8
线程t1将num加到了9
线程t1将num加到了10
线程t1将num加到了11
线程t1将num加到了12
线程t1将num加到了13
线程t1将num加到了14
线程t1将num加到了15
线程t1将num加到了16
线程t1将num加到了17
线程t1将num加到了18
线程t1将num加到了19
线程t1将num加到了20
线程t1将num加到了21
线程t1将num加到了22
线程t1将num加到了23
线程t1将num加到了24
线程t1将num加到了25
线程t1将num加到了26
线程t1将num加到了27
线程t1将num加到了28
线程t1将num加到了29
线程t1将num加到了30
线程t1将num加到了31
线程t1将num加到了32
线程t1将num加到了33
线程t1将num加到了34
线程t1将num加到了35
线程t1将num加到了36
线程t1将num加到了37
线程t1将num加到了38
线程t1将num加到了39
线程t1将num加到了40
线程t1将num加到了41
线程t1将num加到了42
线程t1将num加到了43
线程t1将num加到了44
线程t1将num加到了45
线程t1将num加到了46
线程t1将num加到了47
线程t1将num加到了48
线程t1将num加到了49
线程t1将num加到了50
线程t1将num加到了51
线程t1将num加到了52
线程t1将num加到了53
线程t1将num加到了54
线程t1将num加到了55
线程t1将num加到了56
线程t1将num加到了57
线程t1将num加到了58
线程t1将num加到了59
线程t1将num加到了60
线程t1将num加到了61
线程t1将num加到了62
线程t1将num加到了63
线程t1将num加到了64
线程t1将num加到了65
线程t1将num加到了66
线程t1将num加到了67
线程t1将num加到了68
线程t1将num加到了69
线程t1将num加到了70
线程t1将num加到了71
线程t1将num加到了72
线程t1将num加到了73
线程t1将num加到了74
线程t1将num加到了75
线程t1将num加到了76
线程t1将num加到了77
线程t1将num加到了78
线程t1将num加到了79
线程t1将num加到了80
线程t1将num加到了81
线程t1将num加到了82
线程t1将num加到了83
线程t1将num加到了84
线程t1将num加到了85
线程t1将num加到了86
线程t1将num加到了87
线程t1将num加到了88
线程t1将num加到了89
线程t1将num加到了90
线程t1将num加到了91
线程t1将num加到了92
线程t1将num加到了93
线程t1将num加到了94
线程t1将num加到了95
线程t1将num加到了96
线程t1将num加到了97
线程t1将num加到了98
线程t1将num加到了99
线程t1将num加到了100
线程t2将num加到了101
线程t2将num加到了102
线程t2将num加到了103
线程t2将num加到了104
线程t2将num加到了105
线程t2将num加到了106
线程t2将num加到了107
线程t2将num加到了108
线程t2将num加到了109
线程t2将num加到了110
线程t2将num加到了111
线程t2将num加到了112
线程t2将num加到了113
线程t2将num加到了114
线程t2将num加到了115
线程t2将num加到了116
线程t2将num加到了117
线程t2将num加到了118
线程t2将num加到了119
线程t2将num加到了120
线程t2将num加到了121
线程t2将num加到了122
线程t2将num加到了123
线程t2将num加到了124
线程t2将num加到了125
线程t2将num加到了126
线程t2将num加到了127
线程t2将num加到了128
线程t2将num加到了129
线程t2将num加到了130
线程t2将num加到了131
线程t2将num加到了132
线程t2将num加到了133
线程t2将num加到了134
线程t2将num加到了135
线程t2将num加到了136
线程t2将num加到了137
线程t2将num加到了138
线程t2将num加到了139
线程t2将num加到了140
线程t2将num加到了141
线程t2将num加到了142
线程t2将num加到了143
线程t2将num加到了144
线程t2将num加到了145
线程t2将num加到了146
线程t2将num加到了147
线程t2将num加到了148
线程t2将num加到了149
线程t2将num加到了150
线程t2将num加到了151
线程t2将num加到了152
线程t2将num加到了153
线程t2将num加到了154
线程t2将num加到了155
线程t2将num加到了156
线程t2将num加到了157
线程t2将num加到了158
线程t2将num加到了159
线程t2将num加到了160
线程t2将num加到了161
线程t2将num加到了162
线程t2将num加到了163
线程t2将num加到了164
线程t2将num加到了165
线程t2将num加到了166
线程t2将num加到了167
线程t2将num加到了168
线程t2将num加到了169
线程t2将num加到了170
线程t2将num加到了171
线程t2将num加到了172
线程t2将num加到了173
线程t2将num加到了174
线程t2将num加到了175
线程t2将num加到了176
线程t2将num加到了177
线程t2将num加到了178
线程t2将num加到了179
线程t2将num加到了180
线程t2将num加到了181
线程t2将num加到了182
线程t2将num加到了183
线程t2将num加到了184
线程t2将num加到了185
线程t2将num加到了186
线程t2将num加到了187
线程t2将num加到了188
线程t2将num加到了189
线程t2将num加到了190
线程t2将num加到了191
线程t2将num加到了192
线程t2将num加到了193
线程t2将num加到了194
线程t2将num加到了195
线程t2将num加到了196
线程t2将num加到了197
线程t2将num加到了198
线程t2将num加到了199
线程t2将num加到了200
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TryLock
{
private Lock lock = new ReentrantLock();
public void m1()
{
lock.lock();
try
{
System.out.println("m1拿到锁了");
TimeUnit.SECONDS.sleep(3);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
System.out.println("m1释放锁了");
}
}
public void m2()
{
boolean locked;
locked = lock.tryLock();
if (locked)
{
try
{
System.out.println("m2拿到锁了,m2 执行了");
}
finally
{
lock.unlock();
}
}
else
System.out.println("m2尝试拿到锁失败了。。");
try
{
long startTime = System.currentTimeMillis();
locked = lock.tryLock(4,TimeUnit.SECONDS);
if (locked)
{
long endTime = System.currentTimeMillis();
System.out.println("m2在尝试了" +(endTime - startTime) + "ms 之后拿到了锁,执行了此方法");
}
else
{
long endTime = System.currentTimeMillis();
System.out.println("m2在尝试了" +(endTime - startTime) + "ms 之后依旧没有拿到了锁,执行了此方法");
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
if (locked)
{
lock.unlock();
System.out.println("m2释放了锁");
}
}
}
public static void main(String[] args)
{
TryLock tryLock = new TryLock();
new Thread(tryLock::m1).start();
new Thread(tryLock::m2).start();
}
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestInterrupted
{
private Lock lock = new ReentrantLock();
public void m1()
{
lock.lock();
try
{
System.out.println("m1拿到锁了");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
System.out.println("m1释放锁了");
}
}
public void m2()
{
System.out.println("m2正在尝试拿锁");
lock.lock();
try
{
System.out.println("m2拿到锁了");
}
finally
{
lock.unlock();
System.out.println("m2释放锁了");
}
}
public void m3()
{
try
{
System.out.println("m3正在尝试拿锁");
lock.lockInterruptibly();
try
{
System.out.println("m3拿到锁了");
}
finally
{
lock.unlock();
System.out.println("m3释放锁了");
}
}
//如果尝试拿锁的过程中被打断了,那一定没有拿到锁,所以不需要finally释放锁
catch (InterruptedException e)
{
System.out.println("m3因为长时间拿不到,被外部线程打断了");
}
}
public static void main(String[] args) throws InterruptedException
{
TestInterrupted testInterrupted = new TestInterrupted();
Thread t1 = new Thread(testInterrupted::m1);
Thread t2 = new Thread(testInterrupted::m2);
Thread t3 = new Thread(testInterrupted::m3);
t1.start();
Thread.sleep(1000);
t2.start();
t3.start();
//t2是不能被打断的
t2.interrupt();
Thread.sleep(2000);
//t3可以被中断
t3.interrupt();
}
}