1 synchronized 和 Lock的区别

     a) 存在层次

         synchronized是关键字,在jvm层面上,由内置语言实现。

         Lock是接口、类。

     b) 锁的释放

         synchronized在线程发生异常时会自动释放锁,因此不会发生异常死锁。

         Lock异常时不会自动释放锁,必须手动在finally中释放锁。

     c) 锁的类型和状态

         synchronized是非中断锁(必须等待线程执行完成释放锁)、不可判断(判断是否有锁),非公平(等待的时间不一样)。

         Lock是可中断锁(可以手动中断)、可判断(判断是否有锁),公平(两者皆可)。    

     d) 使用 及 性能

         synchronized用于少量代码的同步。

         Lock用于大量代码的同步。

         Lock锁可以使用读锁提高多线程读效率。

 2 synochronied的缺点

      a) 不能响应进行中断;

      b) 同一时刻无论是读写都只能有一个线程对共享资源进行操作,其他线程只能进行等待。

      c) 锁的释放是由虚拟机内部进行操作,不可进行人工操作干预,既有优点,又有缺点。

          优点就是不用担心会造成死锁,抛出异常后,会自动释放该锁。

          缺点就当有一个线程获取了对应的锁并执行之后,其他线程便只能一直等待,直到线程释放该锁,这样会导致性能偏低

     解决:

        Lock接口的提出就是为了完善synchronized得不完美,变成了jdk层面实现的接口,可以人工灵活的对共享资源变量的操作和控制,无论读还是写的操作。

  3 Lock接口

      源码中Lock接口的方法

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

     a) lock()

            最常用的方法,用来获取锁,如果该锁被其他线程获取之后,将会进入等待的状态。

     b) tryLock()

            有返回值,用来获取锁,如果获取成功返回true,获取失败返回false,执行后就一定会返回,并不会在那里一直等待。

     c) tryLock(long time, TimeUnit unit)

            和tryLock类似,只不过在拿不到锁的时候,会等待一段时间。此期间之间还拿不到的话,就会返回false。只要拿到了锁的话,就会返回true。

     d) lockInterruptibly()

            此方法和以上不同的是可以中断其等待的状态。

            例如两个线程同时通过lock.lockInterruptibly()想获得某个锁时,假如A线程获得了该锁,B线程处于等待的状态,这是线程B就可以调用threadB.interrupt()方法来中断线程B的等待状态。    

            可中断,不必再和synchronized修饰的代码一样,当线程处于等待的时候不必一直等待。

        注意:

            因为lockInterruptibly()在生命的时候就已经抛出了异常,所以lock.lockInterruptibly()必须在try块中,或者在调用lockInterruptibly()方法外声明抛出InterrupExcption异常

   4 ReentrantLock()类

        ReentratLock是唯一实现了Lock接口的类,除了Lock接口中的方法,ReentrantLock还提供了更多的方法。

    //传入boolean值,true时create一个公平锁,false为非公平锁
    ReentrantLock(boolean fair) 
    
    //查看有多少线程等待锁
    int getQueueLength()
	
    //是否有线程等待抢锁
    boolean hasQueuedThreads()
	
    //是否有指定线程等待抢锁
    boolean hasQueuedThread(Thread thread)
	
    //当前线程是否抢到锁。返回0代表没有
    int getHoldCount()
	
    //查询此锁是否由任何线程持有
    boolean isLocked()
	 
    //是否为公平锁
    boolean isFair()

        ReentrantLock中Condition的使用

public interface Condition {
    /**
     *Condition线程进入阻塞状态,调用signal()或者signalAll()再次唤醒,
     *允许中断如果在阻塞时锁持有线程中断,会抛出异常;
     *重要一点是:在当前持有Lock的线程中,当外部调用会await()后,ReentrantLock就允许其他线程来抢夺锁当前锁,
     *注意:通过创建Condition对象来使线程wait,必须先执行lock.lock方法获得锁
     */
     void await() throws InterruptedException;

     //Condition线程进入阻塞状态,调用signal()或者signalAll()再次唤醒,不允许中断.如果在阻塞时锁持有线程中断,继续等待唤醒
     void awaitUninterruptibly();

     //设置阻塞时间,超时继续,超时时间单位为纳秒,其他同await();
     //返回时间大于零,表示是被唤醒,等待时间并且可以作为等待时间期望值,小于零表示超时	
     long awaitNanos(long nanosTimeout) throws InterruptedException;
	
     //类似awaitNanos(long nanosTimeout);返回值:被唤醒true,超时false
     boolean await(long time, TimeUnit unit) throws InterruptedException;

     //类似await(long time, TimeUnit unit) 
     boolean awaitUntil(Date deadline) throws InterruptedException;

     //唤醒指定线程
     void signal();

     //唤醒全部线程
     void signalAll();
  }

         Condition 是Lock的一个条件,可以多次new Condition()获得多个条件.

         Condition可用于线程间通信,通过Condition能更精细的控制休眠和唤醒,在粒度和性能上都优于Object的通信方法(wait()、notify() 和 notifyAll());

         增加了线程中断、阻塞超时的函数;

         使用ReentrantLock.Condition线程通信注意点:

            1  使用ReentrantLock.Condition的signal()、await()、signalAll()方法之前必须先进行lock()的操作,finally中unlock()

                类似于使用notifyAll()、wait()、notifyAll()等方法前需要有synchronized,否则会抛出IIIegalMonitorStateException异常

            2 使用ReentrantLock.Condition的signal()、await()、signalAll()方法不能和Object的notifyAll()、wait()、notifyAll()方法混用,否则也会抛出IIIegalMonitorStateException异常

    5 公平锁与非公平锁

        公平锁:

            是指多个线程竞争同一资源时[等待同一个锁]时,获得资源的顺序时按照申请锁的先后的顺序;保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁。

            特点:

                线程执行会严格按照顺序执行,等待锁的线程不会饿死,但是整体的效率会降低。

        非公平性:   

            是指多个线程竞争同一资源时,获取资源的顺序是不确定的,一般是抢占式的;非公平锁相对公平锁式增加了获取资源的不确定性,但是整体效率得以提升。

            特点:

                整体效率高,线程等待的时间片不是确定的;

       实现:   

         ReentrantLock fairLock = new ReentrantLock(true);

         ReentrantLock unFairLock = new ReentrantLock();   

            当构造函数里面传入的是true时,此锁是公平的;传入是false时,此锁是不公平的;

    6 可重入锁

        可重入性,其实就是锁的分配的机制,就是当一个线程执行到synchronized修饰的test1方法时,test1中又会调用test2方法,此时线程不必再去申请锁,而是可以直接去执行。

  class MyClass {
      public synchronized void test1() {
         test2();
      }
     
      public synchronized void test2() {
         
      }
  }

        synchronized和Lock都具备可重入性。

    7 读写锁

          读写锁将对一个资源(比如文件)的方法分成了两个锁,一个读锁和一个写锁。

          也正是存在读写锁,才使得多个线程之间的读操作不会发生冲突。

          ReadWriteLock就是读写锁,也是一个接口,ReentrantReadWriteLock实现了这个接口,可以通过readLock()获取读锁,writeLock()获取写锁。

         注意:

                1. 多个读这可以同时进行读。

                2. 只允许写者一个进行,也不能同时进行。

                     如果同时存在,读者必须等写者进行完,再执行,唤醒时也是写者优先(写者优先于读者)

          例:

public class Demo{
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     
    public static void main(String[] args)  {
        final Demo demo= new Demo();
         
        new Thread(){
            public void run() {
                demo= (Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                demo= (Thread.currentThread());
            };
        }.start();
         
    }  
     
    public void get(Thread thread) {
        rwl.readLock().lock();
        try {
            long start = System.currentTimeMillis();
             
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(thread.getName()+"正在进行读操作");
            }
            System.out.println(thread.getName()+"读操作完毕");
        } finally {
            rwl.readLock().unlock();
        }
    }
}

    

     结果 :

          Thread-0正在进行读操作

          Thread-0正在进行读操作

          Thread-1正在进行读操作

          Thread-0正在进行读操作

          Thread-0正在进行读操作

          Thread-1正在进行读操作

          Thread-0正在进行读操作

          Thread-1正在进行读操作

          Thread-0读操作完毕 

          Thread-1读操作完毕    

    

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐