• java多线程---重入锁ReentrantLock


    1.定义
    重入锁ReentrantLock,支持重入的锁,表示一个线程对资源的重复加锁。
    
    2.底层实现
    每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁。
    
    3.使用样例

    eg:

    import java.util.concurrent.locks.ReentrantLock;
    
    public class ReentrantLockTest implements Runnable{
    	public static int i = 100;
    	public static ReentrantLock rl = new ReentrantLock();
    	
    	@Override
    	public void run() {
    		while(true){
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			rl.lock();
    			if(i>0){
    				System.out.println(Thread.currentThread().getName() + " : " + i--);
    			}
    			rl.unlock();
    		}
    	}
    	
    	public static void main(String[] args) {
    		ReentrantLockTest rlt = new ReentrantLockTest();
    		Thread t1 = new Thread(rlt);
    		Thread t2 = new Thread(rlt);
    		t1.start();
    		t2.start();
    	}
    
    }
    
    

    中断响应
    synchronized:线程等待锁,要么获得锁继续执行,要么保持等待。
    ReentrantLock:等待锁的过程中,可以根据需求取消对锁的请求。
    eg:

    import java.util.concurrent.locks.ReentrantLock;
    
    public class IntLock implements Runnable {
    	int lock;
    	public static ReentrantLock lock1 = new ReentrantLock();
    	public static ReentrantLock lock2 = new ReentrantLock();
    	
    	public IntLock(int lock){
    		this.lock = lock;
    	}
    	
    	
    	@Override
    	public void run() {
    		try{
    			if(lock == 1){
    				try{
    					lock1.lockInterruptibly();		// 如果当前线程未被中断,则获取锁。
    				}catch(Exception e){
    					e.printStackTrace();
    				}
    				lock2.lockInterruptibly();
    				System.out.println(Thread.currentThread().getId()+"执行完毕");
    			}else{
    				lock2.lockInterruptibly();
    				try{
    					Thread.sleep(1000);
    				}catch(Exception e){
    					
    				}
    				lock1.lockInterruptibly();
    			}
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			if(lock1.isHeldByCurrentThread()){		//查询当前线程是否保持此锁
    				lock1.unlock();
    			}
    			if(lock2.isHeldByCurrentThread()){		//查询当前线程是否保持此锁
    				lock2.unlock();
    			}
    			System.out.println(Thread.currentThread().getId()+":线程退出");
    		}
    
    	}
    	
    	public static void main(String[] args) throws InterruptedException {
    		IntLock l1 = new IntLock(1);
    		IntLock l2 = new IntLock(2);
    		Thread t1 = new Thread(l1);
    		Thread t2 = new Thread(l2);
    		t1.start();
    		t2.start();
    		Thread.sleep(2000);
    		t2.interrupt();			//中断线程,不中断,则产生死锁
    		
    	}
    
    }
    
    

    锁申请等待限时
    如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁。

    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TimeLock implements Runnable {
    
    	public static ReentrantLock rl = new ReentrantLock();
    	
    	@Override
    	public void run() {
    		try {
    			if(rl.tryLock(5, TimeUnit.SECONDS)){
    				Thread.sleep(6000);
    				System.out.println(Thread.currentThread().getId()+"执行完毕");
    			}else{
    				System.out.println(Thread.currentThread().getId()+"get lock failed");
    			}
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}finally{
    			if(rl.isHeldByCurrentThread())rl.unlock();
    		}
    	}
    	
    	public static void main(String[] args) {
    		TimeLock tl = new TimeLock();
    		Thread t1 = new Thread(tl);
    		Thread t2 = new Thread(tl);
    		t1.start();
    		t2.start();
    	}
    
    }
    
    

    公平锁

    在ReentrantLock中很明显可以看到其中同步包括两种,分别是公平的FairSync和非公平的NonfairSync。公平锁的作用就是严格按照线程启动的顺序来执行的,不允许其他线程插队执行的;而非公平锁是允许插队的。默认情况下ReentrantLock是通过非公平锁来进行同步的,包括synchronized关键字都是如此,因为这样性能会更好。
    

    public ReentrantLock(boolean fair) //如果此锁应该使用公平的排序策略,则该参数为 true

    import java.util.concurrent.locks.ReentrantLock;
    
    public class FairLock implements Runnable{
    
    	public static ReentrantLock rl = new ReentrantLock(true);
    	
    	@Override
    	public void run() {
    		while(true){
    			try {
    				rl.lock();
    				System.out.println(Thread.currentThread().getName()+" : get lock");
    			} catch (Exception e) {
    			}finally{
    				rl.unlock();
    			}
    		}
    	}
    	
    	public static void main(String[] args) {
    		FairLock fl = new FairLock();
    		Thread t1 = new Thread(fl,"Thread_One");
    		Thread t2 = new Thread(fl,"Thread_Two");
    		t1.start();
    		t2.start();
    	}
    
    }
    
    

    在重入锁的实现中,包含3个要素

    1.原子状态,使用CAS操作来存储当前锁的状态,判断锁是否被别的线程持有
    2.等待队列,所有没有请求到锁的线程,会进入到队列进行等待,待有线程释放锁后,系统能从等待队列唤醒一个线程继续工作。
    3.阻塞原语park()和unpark(),用来挂起和恢复线程,没有得到锁的线程会被挂起

    Condition
    Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的。

    Condition函数列表
    void await() //造成当前线程在接到信号或被中断之前一直处于等待状态。
    boolean await(long time, TimeUnit unit) //造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
    long awaitNanos(long nanosTimeout) //造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
    void awaitUninterruptibly() //造成当前线程在接到信号之前一直处于等待状态。
    boolean awaitUntil(Date deadline) //造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
    void signal() //唤醒一个等待线程。
    void signalAll() //唤醒所有等待线程。

    eg:

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ReentrantLockCondition implements Runnable {
    
    	public static ReentrantLock rl = new ReentrantLock();
    	public static Condition condition = rl.newCondition();
    	
    	@Override
    	public void run() {
    		rl.lock();
    		try {
    			condition.await();	//与此 Condition 相关的锁以原子方式释放
    			System.out.println("Thread is going on");
    		} catch (InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}finally{
    			rl.unlock();
    		}
    	}
    	
    	public static void main(String[] args) throws InterruptedException {
    		ReentrantLockCondition rlc = new ReentrantLockCondition();
    		Thread t1 = new Thread(rlc);
    		t1.start();
    		Thread.sleep(3000);
    		rl.lock();
    		condition.signal();
    		rl.unlock();
    	}
    
    }
    
    

    ReentrantLock和synchronized关键字的区别

    1.ReentrantLock在等待锁时可以使用lockInterruptibly()方法选择中断, 改为处理其他事情,而synchronized关键字,线程需要一直等待下去。同样的,tryLock()方法可以设置超时时间,用于在超时时间内一直获取不到锁时进行中断。
    2.ReentrantLock可以实现公平锁,而synchronized的锁是非公平的。
    3.ReentrantLock拥有方便的方法用于获取正在等待锁的线程。
    4.ReentrantLock可以同时绑定多个Condition对象,而synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果要和多于一个条件关联时,只能再加一个额外的锁,而ReentrantLock只需要多次调用newCondition方法即可。

    一般情况下都是用synchronized实现同步,除非下列情况使用ReentrantLock:

    1.某个线程在等待一个锁的控制权的这段时间需要中断
    2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
    3.具有公平锁功能,每个到来的线程都将排队等候

  • 相关阅读:
    mysql支持跨表delete删除多表记录
    三种循环的流程图画法总结
    巧用svn create patch(打补丁)方案解决定制版需求
    svn branch and merge(svn切换分支和合并)详解
    visualvm
    Java -verbose[:class|gc|jni] 转 ----JAVA源码分析
    BootstrapClassloader ExtClassloader AppClassloader
    LINUX 源码+内核所有参数说明
    SDN
    人工智能之数学基础
  • 原文地址:https://www.cnblogs.com/Ch1nYK/p/9245153.html
Copyright © 2020-2023  润新知