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
评论