并发包之重入锁(synchronized的功能扩展)

重入锁ReentrantLock,就是支持重进入的锁,它表示一个该锁能够支持一个线程对资源的重复加锁,除此之外,该锁还支持取锁时的公平和非公平性选择。Synchronized关键字隐式的支持可重入,比如一个Synchronized修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次获得该锁。不会因为获取了锁,下次获取锁的时候就把自己阻塞。

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
public class reentertLock implements Runnable {
//显式的声明一个重入锁.
public static ReentrantLock lock = new ReentrantLock();
public static int i= 0;
@Override
public void run() {
for(int j=0;j<1000;j++)
{ //显式加锁
lock.lock();
try{
i++;
}finally {
lock.unlock(); //显式解锁
}
}
}
public static void main(String[] args) throws Exception
{
reentertLock r1 =new reentertLock();
Thread t1 =new Thread(r1);
Thread t2 =new Thread(r1);
// 线程t1 t2 都执行相同的run函数.
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
}

锁申请等待限时

使用tryLock()方法进行一次限时的等待.

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
// 限时等待锁
public class TimeClock implements Runnable{
public static ReentrantLock lock =new ReentrantLock();
public void run()
{
try
{ //try 接收两个参数,时长和单位.
if(lock.tryLock(5, TimeUnit.SECONDS))
{Thread.sleep(3000);}
else
{System.out.println("get lock failed");}
}
catch (InterruptedException e)
{e.printStackTrace();}
finally
{
if(lock.isHeldByCurrentThread())
{lock.unlock();}
}
}
public static void main(String[] args)
{
TimeClock m1 =new TimeClock();
Thread t1 =new Thread(m1);
Thread t2 =new Thread(m1);
t1.start();
t2.start();
}
}

公平锁

当使用Synchronized时,产生的锁是非公平的,而重入锁允许对其公平性进行设置 它有一个如下的构造函数

1
public ReentrantLock(boolean fair)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class fair_lock implements Runnable{
public static ReentrantLock lock =new ReentrantLock(true);
public void run()
{
while(true)
{
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"获得锁");

}finally {
lock.unlock();
}
}
}
public static void main(String[] args)
{
fair_lock f1 =new fair_lock();
Thread t1 =new Thread(f1,"t1");
Thread t2 =new Thread(f1,"t2");
t1.start();
t2.start();
}
}

重入锁的重要方法

1
2
3
4
5
lock():获得锁,如果锁已经被占用,则等待
lockInterruptibly():获得锁,但优先相应中断.
tryLock(); 尝试获得锁,如果成功,则返回true,失败返回false,该方法不等待,立即返回
tryLock(Long time ,TimeUnit unit);给定时间内尝试获得锁
unlock()释放锁.

Condition条件( 重入锁的好搭档)

Condition 对象和wait()和notify()的作用是大致相同的,但是wait()和notify()是和Synchronized关键字合作使用的,而condition 是和重入锁相关联的

通过Lock接口的Condition new Condition()方法就可以生成一个与当前重入锁绑定的Condition 实例,

通过condition 对象,我们就可以让线程在合适的时间等待,或者在合适的时刻得到通知,继续执行

Condition 接口的基本方法如下:

1
2
3
4
5
6
7
8
9
10
11
void  await();
使当前线程等待,同时释放当前锁,当其他线程中使用signal()或者signalAll()方法时,线程会重新获得锁并继续执行,或者当线程中断时,也能跳出等待.
void awaitUninterruptibly();
和await()方法基本相同,但是不会在等待过程中相应中断
long awaitNanos(long nanosTimeout)
boolean await(long time,TimeUnit unit)
boolean awaitUntil(Date deadline)
void signal();
用于唤醒一个等待中的线程.
void signalAll();
唤醒所有等待中的线程.
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
public class Condition_example implements Runnable {
public static ReentrantLock lock =new ReentrantLock();
//通过lock 生成condition 对象
public static Condition condition =lock.newCondition();
public void run()
{
try
{
lock.lock();
//线程在condition对象上等待.
condition.await();
System.out.println("Thread is going on");
}catch (InterruptedException e)
{
e.printStackTrace();
}
finally {
lock.unlock();
}
}
public static void main(String[] args) throws Exception
{
Condition_example conditionExample =new Condition_example();
Thread t1 =new Thread(conditionExample);
t1.start();
Thread.sleep(2000);
lock.lock();
//主线程发出通知,告知等待在Condition上的线程现在可以继续执行了.
condition.signal();
lock.unlock();
}
}

和wait和notify方法一样,当线程使用condition.await()时,要求线程持有相关的重入锁.