1、使用ReentrantLock类
它与synchronized有相似的功能效果,使用lock方法开启同步区域的起始边界,使用unlock方法开启同步区域的结束边界,两个方法成对出现。synchronized使用wait/notify或wait/notifyAll可以实现等待/通知模型。ReentrantLock类也可以借助Condition类的对象,实现等待/通知模型。Object类的wait方法相当于Condition类的await方法,Object类的notify方法相当于Condition类的signal方法,Object类的notifyAll方法相当于Condition类的signalAll方法。在同一个锁下,Condition对象可以有多个。
多生产者/多消费者双条件的等待通知模型示例代码:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Resource {
private static int resource=0;
private static int top=10;
private static int low=0;
public static final ReentrantLock lock=new ReentrantLock();
public static final Condition wc=lock.newCondition();
public static final Condition rc=lock.newCondition();
public static int getResource() {
return resource;
}
public static void setResource(int resource) {
Resource.resource = resource;
}
public static int getLow() {
return low;
}
public static void setLow(int low) {
Resource.low = low;
}
public static int getTop() {
return top;
}
public static void setTop(int top) {
Resource.top = top;
}
}
public class Producer implements Runnable{
@Override
public void run() {
Resource.lock.lock();
if(Resource.getResource()>=Resource.getTop()) {
System.out.println(Thread.currentThread().getName()+" producer thread is waiting");
try {
Resource.wc.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(Resource.getResource()<Resource.getTop()) {
Resource.setResource(Resource.getResource()+1);
System.out.println(Thread.currentThread().getName()+" producer thread is working");
Resource.rc.signalAll();
}
Resource.lock.unlock();
}
}
public class Consumer implements Runnable{
@Override
public void run() {
Resource.lock.lock();
if(Resource.getResource()<=Resource.getLow()) {
System.out.println(Thread.currentThread().getName()+" comsumer thread is no resource to buy, so notify all thread");
try {
Resource.rc.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(Resource.getResource()>Resource.getLow()) {
Resource.setResource(Resource.getResource()-1);
System.out.println(Thread.currentThread().getName()+" comsumer thread is buying");
Resource.wc.signalAll();
}
Resource.lock.unlock();
}
}
import java.util.ArrayList;
public class CPTest {
public static void main(String[] args) {
Consumer c=new Consumer();
Producer p=new Producer();
ArrayList<Thread> list =new ArrayList<Thread>();
for(int i=0;i<30;i++) {
list.add(new Thread(p));
list.add(new Thread(c));
}
for(int i=0;i<list.size();i++) {
list.get(i).start();
}
}
}
锁Lock分为公平锁和非公平锁,公平锁表示获取锁的顺序是按照线程加锁的顺序来分配的,即FIFO;非公平锁是一种获取锁的抢占机制,随机获得,可能会导致某些线程一直拿不到锁。非公平锁的运行结果基本上是乱序的,公平锁的运行结果基本呈现有序状态。
getHoldCount方法是查询当前线程保持此锁定的个数,即调用lock方法的次数。
getQueueLength方法是查询等待获取当前锁的线程估计数。
getWaitQueueLength(Condition condition)是查询等待lock相关condition的线程估计数。
hasQueueThread(Thread thread)是查询线程thread是否正在等待获取此lock。
hasQueueThreads方法是查询是否有线程正在等待获取此锁。
hasWaiters(Condition condition)查询是否有线程正在等待与此lock有关的condition条件。
isFair方法是判断此lock是不是公平锁。
isHeldByCurrentThread方法是查询当前线程是否保持此lock。
isLocked方法是查询此锁定是否由任意线程保持。
lockInterruptiby方法:如果线程未被中断,则获取锁定;如果已经被中断,则抛出异常。
tryLock方法:仅在调用时,lock没有被另外一个线程保持的情况下,才获取lock。
tryLock(long timeout,TimeUnit unit):如果lock在给定的时间内没有被另一个线程保持,且当前线程未被中断,则获取该lock。
condition.awaitUntil(Date date):程序运行至此方法处会进入等待状态,如果在date时间段内没有被唤醒,则date之后自动唤醒,继续后续执行。如果在date时间段内被唤醒,则继续后续执行。如果被唤醒后没有拿到lock,则等待获取cpu执行时间。
2、使用ReentrantReadWriteLock类
这个类是读写锁机制,与读操作相关的锁也称为共享锁,与写操作相关的锁也称为排他锁,除了读锁与读锁之间不互斥,其他情况均互斥。这种锁特别适合读写分离,并且读操作的占比越高,运行效率越高。
读锁加锁机制:
(new ReentrantReadWriteLock()).readLock().lock();
//同步区域
(new ReentrantReadWriteLock()).readLock().unlock();
写锁加锁机制:
(new ReentrantReadWriteLock()).writeLock().lock();
//同步区域
(new ReentrantReadWriteLock()).writeLock().unlock();
3、锁的分类
锁分为显式锁和隐式锁,显式锁就是ReentrantLock和ReentrantReadWriteLock,隐式锁是synchronized。隐式锁是不公平锁,显式锁默认是不公平锁,但是可以支持公平锁。隐式锁synchronized是不可中断的,Lock的是可以中断的。从使用方式上来讲,隐式锁synchronized操作上更加容易;显式锁在操作上需要更加细致,如加锁和释放锁,需要跟try...catch...finally联用。