为了解决线程同步问题我们经常使用synchronized关键字和Lock接口下的锁来解决同步问题。但是这两者有什么区别之处呢?首先我们分别使用synchronized和Lock来解决生产者消费者问题。
生产者消费者问题 当生产者生产数量为1的时候,通知消费者消费。当生产者产品数量为0的时候,生产产品。
synchronized 
运行测试,结果符合预期!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public  class  A      public  static  void  main (String[] args)           Data data = new  Data();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 try  {                     data.increment();                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         }, "A" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 try  {                     data.decrement();                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         }, "B" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 try  {                     data.increment();                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         }, "C" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 try  {                     data.decrement();                 } catch  (InterruptedException e) {                     e.printStackTrace();                 }             }         }, "D" ).start();     } } class  Data      private  int  number = 0 ;          public  synchronized  void  increment ()  throws  InterruptedException          while  (number != 0 ) {                          this .wait();         }         number++;         System.out.println(Thread.currentThread().getName() + "=>"  + number);                  this .notifyAll();     }          public  synchronized  void  decrement ()  throws  InterruptedException          while  (number == 0 ) {                          this .wait();         }         number--;         System.out.println(Thread.currentThread().getName() + "=>"  + number);                  this .notifyAll();     } } 
Lock 要想使用Lock达到线程阻塞唤醒,我们必须要使用到Condition
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。
Condition是个接口,基本的方法就是await()和signal()方法。
Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()。调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用 
Conditon中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()
 
运行查看结果,符合预期!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public  class  B      public  static  void  main (String[] args)           Data2 data = new  Data2();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 data.increment();             }         }, "A" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 data.decrement();             }         }, "B" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 data.increment();             }         }, "C" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 data.decrement();             }         }, "D" ).start();     } } class  Data2      private  int  number = 0 ;     Lock lock = new  ReentrantLock();     Condition condition = lock.newCondition();          public  void  increment ()                    lock.lock();         try  {             while  (number != 0 ) {                                  condition.await();             }             number++;             System.out.println(Thread.currentThread().getName() + "=>"  + number);                          condition.signalAll();         } catch  (InterruptedException e) {             e.printStackTrace();         } finally  {                          lock.unlock();         }     }          public  void  decrement ()                    lock.lock();         try  {             while  (number == 0 ) {                                  condition.await();             }             number--;             System.out.println(Thread.currentThread().getName() + "=>"  + number);                          condition.signalAll();         } catch  (InterruptedException e) {             e.printStackTrace();         } finally  {                          lock.unlock();         }     } } 
两者对应关系 
synchroized和Lock的区别 原始构成 synchronized是java关键字属于JVM层面,底层是通过monitor对象来完成,其实wait/notify等方法也依赖于monitor对象。只有在同步代码块中或方法中才能调用wait/notify等方法。
Lock是具体类(java.util.concurrent.locks.Lock)是api层面的锁。
获取锁的状态 synchornized无法判断是否获取锁的状态
Lock可以通过·tryLock()判断是否获取到锁。
使用方法 synchronized不需要用户去手动释放锁,当synchronized代码执行完成后系统会自动让线程释放对锁的占用。
Lock则需要用户去手动释放锁,否则可能出现没有主动释放锁而导致的死锁现象。所以lock()和unlock()方法配合try/finally语句块来完成。
异常是否释放锁 synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁
lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)
等待是否可中断 synchronized不可中断,除非抛出异常或者正常运行完成。
Lock可中断
通过设置超时方法中断tryLock(long timeout,TimeUnit unit) 
通过lockInterruptibly() 
 
加锁是否公平 synchronized非公平锁
ReentrantLock两者都可以,默认非公平锁,构造方法传入boolean值,true为公平锁,false为非公平锁。
是否一直等待 使用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去。(不可中断)
Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束啦。(可以中断)
能否精确唤醒线程 synchronized不能,只能随机唤醒一个(notify)或者唤醒全部等待线程(notifyall)
Lock可以使用condition达到精确唤醒
性能问题 在性能上来说,如果竞争资源不够激烈,两者的性能是差不多的,而当竞争资源非常激烈是(有大量线程同时竞争),此时Lock的性能远远优于synchronzed。
synchronized适合锁代码少量的同步问题
Lock锁适合大量同步的代码的同步问题。
精准唤醒 使用Lock加Condition精准唤醒,完成A执行完调用B,B执行完调用C,C执行完调用A。
运行测试。查看结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 public  class  C      public  static  void  main (String[] args)           Data3 data = new  Data3();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 data.printA();             }         }, "A" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 data.printB();             }         }, "B" ).start();         new  Thread(() -> {             for  (int  i = 0 ; i < 10 ; i++) {                 data.printC();             }         }, "C" ).start();     } } class  Data3      private  Lock lock = new  ReentrantLock();          private  Condition condition1 = lock.newCondition();     private  Condition condition2 = lock.newCondition();     private  Condition condition3 = lock.newCondition();     private  int  number = 1 ;     public  void  printA ()           lock.lock();         try  {             while  (number != 1 ) {                                  condition1.await();             }             System.out.println(Thread.currentThread().getName() + "=>AAAAAAAA" );             number = 2 ;                          condition2.signal();         } catch  (Exception e) {             e.printStackTrace();         } finally  {             lock.unlock();         }     }     public  void  printB ()           lock.lock();         try  {             while  (number != 2 ) {                                  condition2.await();             }             System.out.println(Thread.currentThread().getName() + "=>BBBBBBBB" );             number = 3 ;                          condition3.signal();         } catch  (Exception e) {             e.printStackTrace();         } finally  {             lock.unlock();         }     }     public  void  printC ()           lock.lock();         try  {             while  (number != 3 ) {                                  condition3.await();             }             System.out.println(Thread.currentThread().getName() + "=>CCCCCCCC" );             number = 1 ;                          condition1.signal();         } catch  (Exception e) {             e.printStackTrace();         } finally  {             lock.unlock();         }     } }