java多线程-java ReentrantLock

sancaiodm Java 2021-09-13 860 0

 内部锁: 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,说明并发时出现了数据覆盖之类的操作,

image.png

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

image.png


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



评论