一、ReentrantLock可重入锁
Lock lock = new ReentrantLock();
lock.lock();
手动加锁必须手动释放锁。即使线程中途抛出异常也不会释放锁,因此一般在finally里lock.unlock();
ReentrantLock和synchronized的区别在于ReentrantLock可以尝试获取锁,而synchronized获取锁后会使后面的线程阻塞;此外synchronized是非公平锁,而ReentrantLock可以指定为公平锁。
boolean locked = lock.tryLock();
二、lock()与lockInterruptibly()
两者都是获取锁,lock()的锁不能被打断,lockInterruptibly()的锁可以被interrupt()打断。当一个线程占用锁的时间过长,而不希望后面的线程长时间等待而想打断后面线程的等待时,如果该可重入锁是用lockInterruptibly()上锁的,可以用interrupt()打断。
三、实现一个固定大小的同步容器,具有set(),get(),size()方法
方法一:synchronized,wait,notifyAll
public class MyContainer1<T> {
private LinkedList<T> lists = new LinkedList<>();
private int MAX = 10;
private int count = 0;
public synchronized void put(T t){
while(lists.size() == MAX){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lists.add(t);
++count;
this.notifyAll();
}
public synchronized T get(){
while (lists.size() == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = lists.removeFirst();
--count;
this.notifyAll();
return t;
}
public static void main(String[] args) {
MyContainer1<String> container = new MyContainer1<>();
//启动消费者线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {
System.out.println(container.get());
}
}, "consumer"+i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//启动生产者线程
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 25; j++) {
container.put(Thread.currentThread().getName() + " " + j);
}
}, "producer"+i).start();
}
}
}
方法二:ReentrantLock,Condition,await,signalAll
public class MyContainer2<T> {
private int MAX = 10;
private int count = 0;
private LinkedList<T> list = new LinkedList<>();
private ReentrantLock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
public void put(T t){
//满了就await
try {
lock.lock();
while (list.size() == MAX) {
producer.await();
}
list.add(t);
count++;
consumer.signalAll();
} catch (InterruptedException e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
public T get(){
T t = null;
try {
lock.lock();
while (list.size() == 0) {
consumer.await();
}
t = list.removeFirst();
count--;
producer.signalAll();
} catch (InterruptedException e){
e.printStackTrace();
} finally {
lock.unlock();
return t;
}
}
public static void main(String[] args) {
MyContainer2<String> container = new MyContainer2<>();
//启动消费者线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {
System.out.println(container.get());
}
}, "consumer"+i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//启动生产者线程
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 25; j++) {
container.put(Thread.currentThread().getName() + " " + j);
}
}, "producer"+i).start();
}
}
}
需要注意的是使用可重入锁的这种情况,try catch不能只包住await一行,需要包住整块代码,否则会出错。