高级concurrent包
线程同步:
1. 多线程读写竞争资源需要同步
2. Java语言提供了synchronized/wait/notify来实现线程的同步
3. 但是编写多线程的同步仍然很困难
所以JDK提供的高级的java.util.concurrent包
1. concurrent包可以提供更高级的同步功能
2. 同时可以简化多线程程序的编写
3. JDK1.5提供的
因为synchronized是JAVA提供的,所以不需要考虑异常.
但是如果我们使用ReentrantLock,是一个普通的JAVA类,所以我们要用try...finally的这样的结构来保证
锁的释放,
首先我们new ReentrankLock()来创建一个对象,然后我们要试图用lock.lock()方法获取锁,如果我们
获取成功,就进入try代码,在finally里面使用lock.unlock()来释放锁.这里必须要注意的是lock.lock()
必须在try之外完成.因为lock.lock()方法可能会失败,而unlock方法一定要在finally里面完成
ReentrantLock也是一种可重入的锁:
1. 可重入的锁,一个线程可多次获取同一个锁
2. lock()方法就是用来获取锁的
3. tryLock()方法可尝试获取锁并可指定超时
使用ReentrankLock比synchronized更安全
synchronized要么获得锁,要么永远的等待下去,而使用ReentrankLock的时候,我们可以使用tryLock()
在失败的时候,不会导致死锁.
package com.leon.day05;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
// 首先初始化一个ReentrantLock实例,然后向上转型为Lock接口
private Lock lock = new ReentrantLock();
private int value = 0;
public void add(int m) {
lock.lock();
try {
this.value += m;
}finally {
lock.unlock();
}
}
public void dec(int m) {
lock.lock();
try {
this.value -= m;
}finally {
lock.unlock();
}
}
public int get() {
lock.lock();
try {
return this.value;
}finally {
lock.unlock();
}
}
}
public class Main {
final static int LOOP = 100;
// 在main方法中启动两个线程,分别调用add和dec方法,
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread() {
@Override
public void run() {
for(int i=0;i<LOOP;i++) {
counter.add(1);
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
for(int i=0;i<LOOP;i++) {
counter.dec(1);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.get());
}
}
ReentrantLock可以替代synchronized
ReentrantLock获取锁更安全
必须使用try...finally保证正确获取和释放锁
临界区就是任何时候只有一个线程能够执行的代码块,但是有些时候这种保护有点过头
任何时候只允许一个线程修改,对inc()方法加锁也是必须的,但是get()方法只读取数据,不修改数据,
允许多个线程同时调用
我们希望允许多个线程同时读,但只要一个线程在写,其他线程就必须等待.
多线程同时读是允许的,我们只是不允许多线程同时写,或者同时读写.
我们可以使用ReadWriteLock可以解决:
1. 只允许一个线程写入,(其它线程既不能写入也不能读取)
2. 但是在没有线程写入的时候,多个线程允许同时读(提高性能)
首先创建一个ReadWriteLock的实例,然后分别获取readLock()和writeLock().
变量rLock代表readLock,而wLock代表writeLock,在写方法中使用wLock.lock()加锁
而在读方法中使用rLock.lock()加锁,这样我们就可以大大的提高读取的效率.
package com.leon.day05;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class Counter {
// 首先创建一个ReadWriteLock的实例
private ReadWriteLock lock = new ReentrantReadWriteLock();
// 然后通过readLock获取读锁
private Lock rLock = lock.readLock();
// 通过writeLock获取写锁
private Lock wLock = lock.writeLock();
private int value = 0;
// add方法是一个修改方法,所以用wLock,解锁加锁
public void add(int m) {
wLock.lock();
try {
this.value += m;
}finally {
wLock.unlock();
}
}
// 对于dec方法也是写锁
public void dec(int m) {
wLock.lock();
try {
this.value -= m;
}finally {
wLock.unlock();
}
}
// 但是对于get方法只需要使用一个读锁,这样多个线程就可以获取读锁
public int get() {
rLock.lock();
try {
return this.value;
}finally {
rLock.unlock();
}
}
}
public class Main {
final static int LOOP = 100;
// 在main方法中启动两个线程,分别调用add和dec方法,
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread() {
@Override
public void run() {
for(int i=0;i<LOOP;i++) {
counter.add(1);
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
for(int i=0;i<LOOP;i++) {
counter.dec(1);
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.get());
}
}
ReadWriteLock适用的条件:
1. 适用同一个实例,有大量线程读取,仅有少数线程修改:例如一个论坛的帖子,浏览可以看成是读取,但是
非常频繁的,发布写入是不频繁的. 这种情况下就可以使用ReadWriteLock.
使用ReadWriteLock可以提高读取的效率
1. ReadWrite只允许一个线程写入
2. 但是允许多个线程同时读取
3. 特别适合读多写少的场景