Skip to content

Lock锁详解 #9

@jingegebuguai

Description

@jingegebuguai

Lock

Lock与Synchronized区别

  • Lock非Java内置特性,而Synchronized是Java语言的关键字,因此是内置特性。Lock是一个接口,通过这个接口可以实现同步访问。
  • Synchronized不需要手动释放锁,当Synchronized方法或代码块执行完成后,系统会自动让线程释放对锁的占用;Lock需要用户手动释放锁,如果没有释放锁,会导致死锁现象。

Lock接口的使用

	public interface lock { 
		void lock();
		void lockInterruptibly() throws InterruptException;
		boolean trylock();
		boolean trylock(long time, TimeUnit unit) throws InterruptException;
		void unlock();
		Condition newCondition();
	}

unlock()作用是释放锁。最常用获取锁的方法是lock(),lock必须被显式的被创建,锁定和释放。一般使用ReentrantLock实例化,为了保证锁最后一定释放,要把互斥区放到try块中,并在finally语句中释放锁。当有return语句时,把return语句放到try子句中,确保unlock()不会过早执行。

	//默认为非公平锁,若使用公平所,需要传入参数true
	Lock lock = new ReentrantLock();
	···
	lock.lock();
	try{
		//执行任务,更新状态
	} catch(Exception e){
		...
	} finally{
		lock.unlock();
	}

tryLock()返回值是布尔型,trylock(long time, TimeUnit unit)方法和tryLock()类似,区别就是这个方法拿不到锁会等待一段时间,如果在时间期限内还拿不到锁就返回false。

	Lock lock = new ReentrantLock();
	···
	if(lock.tryLock()){
		try{
			//执行任务,更新状态
		}catch(Exception e){
			···
		} finally{
			lock.unlock();
		}
	}else{
		···
	}

lockInterruptibly()与以上方法不同,这个方法获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程等待状态。例如:线程A,线程B都想获取到锁,此时A获取到锁,B只能等待,但是B也可以调用threadB.interrupt()中断等待状态。

	public void method() throws InterruptedException(){
		lock.interruptibly();
		try{
			//执行任务,更新状态
		}catch(Exception e){
			···
		} finally{
			lock.unlock();
		}
	}

已获取到锁的线程无法interrupt()中断,interrupt()只能中断阻塞状态的线程。

以上是简略的用法,完整的使用方法如下:

对lock()方法执行代码如下:

	import java.util.ArrayList;
	import java.util.List;
	import java.util.concurrent.locks.Lock;
	import java.util.concurrent.locks.ReentrantLock;
	import java.util.stream.Stream;

	public class Main{
	    private List<Integer> list = new ArrayList<>();
	    (1//private Lock lock = new ReentrantLock();
	    public void method(Thread thread){
	    	(2//Lock lock = new ReentrantLock();
	        lock.lock();
	        try{
	            System.out.println(thread.getName()+"获取线程");
	            Stream.of(1,2,3,4,5).forEach(e -> list.add(e));
	            System.out.println(list);
	        }catch (Exception e){
	            e.printStackTrace();
	        }finally {
	            System.out.println(thread.getName()+"释放线程");
	            lock.unlock();
	        }
	    }

	    public static void main(String[] args) {
	        Main test = new Main();
	        new Thread(){
	            @Override
	            public void run(){
	                super.run();
	                test.method(Thread.currentThread());
	            }
	        }.start();
	        new Thread(){
	            @Override
	            public void run() {
	                super.run();
	                test.method(Thread.currentThread());
	            }
	        }.start();
	    }
	}

若是去掉(2)处注释,执行代码,执行结果如下:

Thread-0获取线程
Thread-1获取线程
[null, 1, 2, 2, 3, 3, 4, 5, 5]
[null, 1, 2, 2, 3, 3, 4, 5, 5]
Thread-1释放线程
Thread-0释放线程

为何Thread-0还未释放,Thread-1就获取到锁了呢?因为注释(2)处的lock是局部变量,我们两个线程执行时获取到的lock锁不是同一个锁,所以没有冲突。而(1)处的lock是仅此一家啊!

所以去掉(1)处的注释获取到的结果如下:

Thread-0获取线程
[1, 2, 3, 4, 5]
Thread-0释放线程
Thread-1获取线程
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
Thread-1释放线程

下面对tryLock()方法执行代码如下:

	import java.util.ArrayList;
	import java.util.List;
	import java.util.concurrent.locks.Lock;
	import java.util.concurrent.locks.ReentrantLock;
	import java.util.stream.Stream;

	public class Main{
	    private List<Integer> list = new ArrayList<Integer>();
	    private Lock lock = new ReentrantLock();
	    public void method(Thread thread){
	        if(lock.tryLock()){
	            try{
	                System.out.println(thread.getName()+"获取线程");
	                Stream.of(1,2,3,4,5).forEach(e -> list.add(e));
	                System.out.println(list);
	            }catch (Exception e){
	                e.printStackTrace();
	            }finally {
	                System.out.println(thread.getName()+"释放线程");
	                lock.unlock();
	            }
	        }else{
	            System.out.println(thread.getName()+"获取线程失败");
	        }
	    }

	    public static void main(String[] args) {
	        Main test = new Main();
	        new Thread(){
	            @Override
	            public void run(){
	                super.run();
	                test.method(Thread.currentThread());
	            }
	        }.start();
	        new Thread(){
	            @Override
	            public void run() {
	                super.run();
	                test.method(Thread.currentThread());
	            }
	        }.start();
	    }
	}

tryLock()执行结果如下:

Thread-1获取线程
Thread-0获取线程失败
[1, 2, 3, 4, 5]
Thread-1释放线程	

对lockInterruptibly执行测试代码如下:

	import java.util.ArrayList;
	import java.util.List;
	import java.util.concurrent.locks.Lock;
	import java.util.concurrent.locks.ReentrantLock;
	import java.util.stream.Stream;

	class thread1 implements Runnable{
	    private Main test = null;
	    public thread1(Main test){
	        this.test = test;
	    }
	    @Override
	    public void run() {
	        try{
	            test.method(Thread.currentThread());
	        }catch(Exception e){
	            System.out.println(Thread.currentThread()+"线程被中断");
	        }
	    }
	}

	public class Main{
	    Lock lock = new ReentrantLock();
	    public void method(Thread thread) throws InterruptedException{
	        lock.lockInterruptibly();
	        try{
	            System.out.println(thread.getName()+"得到了锁");
	            long startTime = System.currentTimeMillis();
	            for(    ;     ;) {
	                if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
	                    break;
	            }
	        }catch (Exception e){
	            e.printStackTrace();
	        }finally {
	            System.out.println(thread.getName()+"释放线程");
	            lock.unlock();
	        }
	    }

	    public static void main(String[] args) {
	        Main test = new Main();
	        Thread thread_1 = new Thread(new thread1(test));
	        Thread thread_2 = new Thread(new thread1(test));
	        thread_1.start();
	        thread_2.start();
	        try{
	            Thread.sleep(2000);
	        }catch (Exception e){
	            e.printStackTrace();
	        }
	        thread_2.interrupt();
	    }
	}

执行结果如下所示:

Thread-0得到了锁
Thread[Thread-1,5,main]线程被中断

ReadWriteLock接口

	public interface ReadWriteLock{
		//return the lock of userd for reading
		Lock readLock();
		//return the lock of userd for writing
		Lock writeLock();
	}

这个接口的两个方法主要用来获取读锁和获取写锁。ReentrantReadWriteLock实现这个接口,并且包含其他很多方法,最主要的还是readLock()和writeLock().这样的用意还是让多个线程可以同时进行读操作.

	import java.util.concurrent.locks.ReentrantReadWriteLock;
	public class test {
	    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
	    public static void main(String[] args) {
	        test _test = new test();
	        new Thread(){
	            @Override
	            public void run() {
	                super.run();
	                _test.method(Thread.currentThread());
	            }
	        }.start();
	        new Thread(){
	            @Override
	            public void run() {
	                super.run();
	                _test.method(Thread.currentThread());
	            }
	        }.start();
	    }
	    public void method(Thread thread){
	        reentrantReadWriteLock.readLock().lock();
	        long start = System.currentTimeMillis();
	        try{
	            while(System.currentTimeMillis()-start<1){
	                System.out.println(thread.getName()+"正在读操作");
	            }
	            System.out.println(thread.getName()+"都操作完成");
	        }catch(Exception e){
	            e.printStackTrace();
	        }finally {
	            reentrantReadWriteLock.readLock().unlock();
	        }
	    }
	}

总结来说,Lock和synchronized有以下几点不同:

  • Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
  • synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
  • Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
  • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
  • Lock可以提高多个线程进行读操作的效率。

在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions