java多线程-java ReentrantLock
内部锁: Java 为我们提供了 synchronized 关键字来实现内部锁,被 synchronized 关键字修饰的方法和代码块就叫同步方法和同步代码块。
显式锁 :(Explict Lock)是 Lock 接口的实例,Lock 接口对显式锁进行了抽象,ReentrantLock 是它的实现类。
各自适用场景
在多个线程持有锁的平均时间不长的情况下我们可以使用内部锁(synchronized)
在多个线程持有锁的平均较长的情况下我们可以使用显式锁(公平锁)(ReentrantLock)
java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。
ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。
(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
ReentrantLock还可以实现公平锁机制。什么叫公平锁呢?也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。
public static ReentrantLock lock = new ReentrantLock(true);//构建的时候传入true 即代表公平锁
lock()与unlock方法都是成对出现,
lock()方法先获取锁的次数,与后面通过unlock()方法释放锁的次数必须保持一致,如果数目不一致,则会造成一直持有,其它线程的堵塞,
public class ReentrantLockTest extends Thread {
public static ReentrantLock lock = new ReentrantLock();
public static int i = 0;
public ReentrantLockTest(String name) {
super.setName(name);
}
@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
lock.lock();
try {
System.out.println(this.getName() + " " + i);
i++;
} finally {
lock.unlock();
}
}
}
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
ReentrantLockTest test1 = new ReentrantLockTest("thread1");
ReentrantLockTest test2 = new ReentrantLockTest("thread2");
test1.start();
test2.start();
//join方法作用:当我们调用某个线程的join方法时,这个方法(join方法)会挂起调用线程(主线程),直到被调用线程结束执行,调用线程才会继续执行。
test2.join();
//主线程会等待test1,test2 子线程运行完后再继续运行。
test1.join();
System.out.println(i); //最后输出
}}
最后的结果是 20000000;如果去掉锁,那么输出结果是一个小于20000000的不确定的数示例:
public class ReentrantLockTest {
private static final Lock lock = new ReentrantLock();
public static int i = 0;
public static void main(String[] args) {
ReentrantLockTest lock = new ReentrantLockTest();
lock.new MyThread("1").start();
lock.new MyThread("2").start();
lock.new MyThread("3").start();
lock.new MyThread("4").start();
}
class MyThread extends Thread{
String name ="";
MyThread(String name){
this.name= name;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for (int j = 0; j < 5000; j++) {
lock.lock();
try {
System.out.println("线程名 "+name + " " + i);
i++;
} finally {
lock.unlock();
}
}
}
};
}各状态测试结果
1 未使用ReentrantLock, 输出结果并不是我们预想的 19999,说明并发时出现了数据覆盖之类的操作,

2 使用ReentrantLock 结果是19999,无错误,但资源的使用并不公平,线程会重复获取锁。如果申请获取锁的线程足够多,那么可能会造成某些线程长时间得不到锁。

附上网上一篇讲解很详细的文章:https://www.cnblogs.com/takumicx/p/9338983.html






评论