之前创建锁的方式中,有说过Lock机制,并且比较了synchronized和lock的区别。本文主要讲解一下lock锁的功能以及实现线程交互。
1.lock()方法与tryLock()方法
lock()方法指占用一个锁,当前线程占用这个对象,则其它线程则只能进入等待状态,而tryLock()方法,则是试图在指定时间内占用当前同步对象,如果占用不了继续等待,其它线程试图抢占。
首先是lock()方法进行加锁,因为lock机制进行加锁,需要手动释放锁,只能在finally中调用lock对象的unlock()方法释放锁。
package waytobuildthread;
import person.Student;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
final Lock lock=new ReentrantLock();
Thread t1= new Thread(){
public void run(){
try
{
lock.lock();
System.out.println("b");
System.out.println("c");
}
finally
{
lock.unlock();
}
}
};
t1.start();
Thread t2= new Thread(){
public void run(){
try
{
lock.lock();
System.out.println("a");
System.out.println("c");
}
finally
{
lock.unlock();
}
}
};
t2.start();
}
}
b
c
a
c
以下是使用tryLock()进行同步对象,因为是尝试加锁,所以需要用一个locked的布尔值进行判断是否加锁,只有加锁了才进行继续操作,以及释放锁操作。首先需要Lock lock=new ReentrantLock();创建lock对象。package person;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;;
public class Student {
public int k=0;
public int total;
public int perMonth;
public String name;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();//创建condition对象
public void saveMoney(Student s) {
// TODO Auto-generated method stub
try
{
lock.lock();
while(total>=3000)
{
try
{
condition.await();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
s.total=s.total+2*(s.perMonth);
System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
condition.signal();
}
finally
{
lock.unlock();
}
}
public void getMoney(Student s)
{
try
{
lock.lock();
while(s.total<=0)
{
try
{
condition.await();//使当前线程暂停,让出占用当前同步对象的锁,并进入等待状态
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
s.total=s.total-s.perMonth;
System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");
condition.signal();//唤醒那些暂停的线程,使其进入等待列表,类似于synchronized下的notify()方法
}
finally
{
lock.unlock();
}
}
}
运行程序,结果如下:
b
c
a
c
2.线程交互
以上讲述了lock锁中的lock方法和tryLock()方法进行创建同步对象的,所以接下来就分别这两个方法进行交互,之前synchronized下的交互是由同步对象的wait()、notify()、notifyAll()三个方法实现的,而上述三个方法只能在synchronized块下进行使用,因此无法在lock锁中使用,而lock锁下实现交互对应的方法是condition对象的await()、signal()、signalAll()方法。
以下先介绍测试程序:
package waytobuildthread;
import person.Student;
public class RunnableTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
final Object obj=new Object();
final Student s=new Student();
final Student jack=new Student();
s.total=1000;
s.perMonth=200;
s.name="Tom";
System.out.println("Tom最开始拥有"+s.total+"元");
Thread[] save=new Thread[10];
Thread[] get=new Thread[10];
for(int i=0;i<10;i++)
{
Thread t1= new Thread(){
public void run(){
s.getMoney(s);;
try {
Thread.sleep(1000);;
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
get[i]=t1;
}
for(int i=0;i<10;i++)
{
Thread t2= new Thread(){
public void run(){
s.saveMoney(s);
try {
Thread.sleep(1000);;
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
};
t2.start();
save[i]=t2;
}
//等待线程结束
for(Thread t:get)
{
try
{
t.join();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
//等待线程结束
for(Thread t:save)
{
try
{
t.join();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
System.out.println("最后Tom总共拥有"+s.total+"元");
}
}
首先使用lock()方法同步下进行交互,需要创建Lock对象,以及condition()对象。注意lock锁需要手动释放锁,即在finally中调用Lock对象的unlock()方法。以下是Student类:
package person;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;;
public class Student {
public int k=0;
public int total;
public int perMonth;
public String name;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();//创建condition对象
public void saveMoney(Student s) {
// TODO Auto-generated method stub
try
{
lock.lock();
while(total>=3000)
{
try
{
condition.await();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
s.total=s.total+2*(s.perMonth);
System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
condition.signal();
}
finally
{
lock.unlock();
}
}
public void getMoney(Student s)
{
try
{
lock.lock();
while(s.total<=0)
{
try
{
condition.await();//使当前线程暂停,让出占用当前同步对象的锁,并进入等待状态
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
s.total=s.total-s.perMonth;
System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");
condition.signal();//唤醒那些暂停的线程,使其进入等待列表,类似于synchronized下的notify()方法
}
finally
{
lock.unlock();
}
}
}
运行测试程序,结果如下:
Tom最开始拥有1000元
Tom取了200当前总共拥有800元
Tom取了200当前总共拥有600元
Tom取了200当前总共拥有400元
Tom取了200当前总共拥有200元
Tom取了200当前总共拥有0元
Tom存了400当前总共拥有400元
Tom存了400当前总共拥有800元
Tom取了200当前总共拥有600元
Tom存了400当前总共拥有1000元
Tom取了200当前总共拥有800元
Tom存了400当前总共拥有1200元
Tom取了200当前总共拥有1000元
Tom存了400当前总共拥有1400元
Tom存了400当前总共拥有1800元
Tom取了200当前总共拥有1600元
Tom取了200当前总共拥有1400元
Tom存了400当前总共拥有1800元
Tom存了400当前总共拥有2200元
Tom存了400当前总共拥有2600元
Tom存了400当前总共拥有3000元
最后Tom总共拥有3000元
以下是使用tryLock()方法的交互,从之前知道,需要设置一个标志进行判断是否占用到了,如果占用到才能进行继续操作。同时需要捕获异常:package person;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;
import java.util.concurrent.locks.Condition;;
public class Student {
public int k=0;
public int total;
public int perMonth;
public String name;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
public void saveMoney(Student s) {
// TODO Auto-generated method stub
boolean locked=false;
try
{
locked=lock.tryLock(1, TimeUnit.SECONDS);
while(total>=3000)
{
try
{
condition.await();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
if(locked)
{
s.total=s.total+2*(s.perMonth);
System.out.println(s.name+"存了"+2*s.perMonth+"当前总共拥有"+s.total+"元");
condition.signal();
}
}
catch(InterruptedException e)
{
e.printStackTrace();
}
finally
{
if(locked)
{
lock.unlock();
}
}
}
public void getMoney(Student s)
{
boolean locked=false;
try
{
locked=lock.tryLock(1, TimeUnit.SECONDS);
while(s.total<=0)
{
try
{
condition.await();//使当前线程暂停,让出占用当前同步对象的锁,并进入等待状态
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
if(locked)
{
s.total=s.total-s.perMonth;
System.out.println(s.name+"取了"+s.perMonth+"当前总共拥有"+s.total+"元");
condition.signal();//唤醒那些暂停的线程,使其进入等待列表,类似于synchronized下的notify()方法
}
}
catch(InterruptedException e)
{
e.printStackTrace();
}
finally
{
if(locked)
{
lock.unlock();
}
}
}
}
运行测试程序,结果如下:
Tom最开始拥有1000元
Tom取了200当前总共拥有800元
Tom存了400当前总共拥有1200元
Tom存了400当前总共拥有1600元
Tom取了200当前总共拥有1400元
Tom取了200当前总共拥有1200元
Tom取了200当前总共拥有1000元
Tom取了200当前总共拥有800元
Tom取了200当前总共拥有600元
Tom取了200当前总共拥有400元
Tom取了200当前总共拥有200元
Tom存了400当前总共拥有600元
Tom取了200当前总共拥有400元
Tom存了400当前总共拥有800元
Tom存了400当前总共拥有1200元
Tom取了200当前总共拥有1000元
Tom存了400当前总共拥有1400元
Tom存了400当前总共拥有1800元
Tom存了400当前总共拥有2200元
Tom存了400当前总共拥有2600元
Tom存了400当前总共拥有3000元
最后Tom总共拥有3000元
根据以上两种方法对应的交互方式,可以看出区别不大,但是也有不同之处:lock()不需要捕获异常,而tryLock()需要捕获异常;lock()不需要判别标志,而tryLock()需要判别标志。