[Java并发]-02-同步和锁

来源:互联网 发布:知悉和悉知的区别 编辑:程序博客网 时间:2024/05/29 18:28

2 同步

2.1 同步问题

临界区:用以访问共享资源的代码块,这个代码块同一时间内只允许一个线程执行。

同步就是为了防止多个线程进入临界区。

当一个线程试图访问临界区的时候,它将使用一种同步机制来查看是不是已经又其他线程进入临界区,如果没有其它线程进入临界区,它就可以进入临界区。如果有线程已经进入临界区它就被线程机制挂起,直到进入的线程离开临界区,如果有等待的线程不止有一个,JVM会选择一个运行,其余的线程继续等待。

两种同步机制

  • synchronized关键字机制
  • Lock接口及其实现机制
static class Counter {private int count;public Counter(int count) {init(count);}public void init(int count) {this.count = count;}public void add() {this.count++;}public void subtraction() {if (this.count > 0) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}this.count--;System.out.println("subtraction " + count);}}public int getCount() {return this.count;}}public static void main(String[] args) {System.out.println("=========main start");final Counter counter = new Counter(10);new Thread() {@Overridepublic void run() {for (int i = 0 ; i < 100; i++) {counter.subtraction();}System.out.println("run1 " + counter.getCount());}}.start();new Thread() {@Overridepublic void run() {for (int i = 0 ; i < 100; i++) {counter.subtraction();}System.out.println("run2 " + counter.getCount());}}.start();System.out.println("=========main end");}

=========main start
=========main end
subtraction 9
subtraction 8
subtraction 7
subtraction 6
subtraction 5
subtraction 4
subtraction 3
subtraction 2
subtraction 1
subtraction 0
run2 0
subtraction -1
run1 -1


发生这种现象的原因是:当counter是1的时候,第一个线程进入subtraction方法,运行到sleep处让出CPU,这时第二个线程也运行到这个方法,进行if判断时候counter仍然是1,进入if块后也sleep,这时两个线程都进入到subtraction方法的sleep处等待,进入前判断大于0的条件都是true;

此时一个线程完成sleep counter--,另一个线程也完成sleep有counter--这样就是-1了

只要将

public synchronized void subtraction()  

结果就会不一样了

=========main start
=========main end
subtraction 9
subtraction 8
subtraction 7
subtraction 6
subtraction 5
subtraction 4
subtraction 3
subtraction 2
subtraction 1
subtraction 0
run2 0
run1 0



2.2 synchronized

Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

原理:

synchronized是有参数的,参数是一个对象,当一个线程执行到synchronized语句时候,JVM会先检查这个参数对象是不是被别的线程占用,如果没有被占用则占用之。

这个参数被称为锁对象,线程获得synchronized的锁对象后,其他针对这个锁对象的synchronized块全都被锁,其他线程若想进入则必须获得该锁对象。


锁的释放

1.线程执行完了当前synchronized块

2.锁对象调用wait()


2.3 Object对象的方法wait() notify() notifyAll()

Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。


wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。

直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。

Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

 


会降低程序的性能,因此尽量让临界区合理的尽可能的最小。

synchronized针对的是对象进行的同步,非静态方法 同步锁对象是this

静态方法的同步锁对象是类的class对象

可以通过非依赖属性,进行多个同步,多个同步锁之间不影响

注意: 方法的synchronized是不继承的!


如果再同步代码块外使用wait()等 JVM会抛出IllegalMonitorStateException


当一个线程调用wait()方法时候JVM将这个线程置入休眠,并且释放控制这个同步代码块的锁对象,同时允许其它线程执行这个对象控制的其它同步代码块。

为了唤醒这个线程,需要在这个锁对象控制的同步代码块中调用notify()或notifyAll()


private byte[] lock = new byte[0];
注:零长度的byte数组对象创建起来将比任何对象都经济。查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。可以作为同步锁对象


2.4wait()

下面是一个synchronized 同步方法和wait()的demo

public class EventStorage {private int maxSize;private LinkedList<Date> storage;/** *  */public EventStorage() {super();this.maxSize = 500;this.storage = new LinkedList<Date>();}public synchronized void set() {while(storage.size() == maxSize) {try {wait();} catch(InterruptedException e) {e.printStackTrace();}}storage.add(new Date());System.out.printf("Set: %d\n", storage.size());notifyAll();}public synchronized void get() {while(storage.size() == 0) {try {wait();} catch(InterruptedException e) {e.printStackTrace();}}System.out.printf("Get: %d, %s\n", storage.size(), storage.poll());notifyAll();}}public class Producer implements Runnable {private EventStorage storage;public Producer(EventStorage storage) {this.storage = storage;}/* * (non-Javadoc) *  * @see java.lang.Runnable#run() */@Overridepublic void run() {for (int i = 0; i < 1000; i++) {storage.set();}}}public class Consumer implements Runnable {private EventStorage storage;public Consumer(EventStorage storage) {this.storage = storage;}/* (non-Javadoc) * @see java.lang.Runnable#run() */@Overridepublic void run() {for (int i = 0; i < 1000; i++) {storage.get();}}}

public static void main(String[] args) {EventStorage storage = new EventStorage();new Thread(new Producer(storage)).start();new Thread(new Consumer(storage)).start();}


可以看看输出理解一下

调用wait()的对象也是synchronized的方法中隐含的同步锁对象 this


然后改改,用一个Object对象作为锁对象

public class EventStorage {private int maxSize;private LinkedList<Date> storage;private Object lock = new Object();/** *  */public EventStorage() {super();this.maxSize = 500;this.storage = new LinkedList<Date>();}public void set() {synchronized (lock) {while (storage.size() == maxSize) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}storage.add(new Date());System.out.printf("Set: %d\n", storage.size());lock.notifyAll();}}public void get() {synchronized (lock) {while (storage.size() == 0) {try {lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.printf("Get: %d, %s\n", storage.size(), storage.poll());lock.notifyAll();}}}

为了说明问题 再继续改

public class EventStorage {private int maxSize;private LinkedList<Date> storage;private Object lock = new Object();public EventStorage() {super();this.maxSize = 2;this.storage = new LinkedList<Date>();}public void set() {synchronized (lock) {while (storage.size() == maxSize) {System.out.println("set while start = " + storage.size());try {System.out.println("set lock.wait() start");lock.wait();// 让获得该锁的当前线程进入等待队列System.out.println("set lock.wait() end");} catch (InterruptedException e) {e.printStackTrace();}System.out.println("set while end = " + storage.size());}storage.add(new Date());System.out.printf("Set: %d\n", storage.size());System.out.println("set lock.notifyAll() start");lock.notifyAll();// 告知该锁的等待队列中的线程System.out.println("set lock.notifyAll() end");}}public void get() {synchronized (lock) {while (storage.size() == 0) {System.out.println("get while start = " + storage.size());try {System.out.println("get lock.wait() start");lock.wait();// 让获得该锁的当前线程进入等待队列System.out.println("get lock.wait() end");} catch (InterruptedException e) {e.printStackTrace();}System.out.println("get while end = " + storage.size());}System.out.printf("Get: %d, %s\n", storage.size(), storage.poll());System.out.println("get lock.notifyAll() start");lock.notifyAll();// 告知该锁的等待队列中的线程,可以来获得CPUSystem.out.println("get lock.notifyAll() end");}}}public class Producer implements Runnable {private EventStorage storage;public Producer(EventStorage storage) {this.storage = storage;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {storage.set();}}}public class Consumer implements Runnable {private EventStorage storage;public Consumer(EventStorage storage) {this.storage = storage;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {storage.get();}}}



public static void main(String[] args) {EventStorage storage = new EventStorage();Thread producer = new Thread(new Producer(storage));Thread consumer = new Thread(new Consumer(storage));producer.start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}consumer.start();}

Set: 1
set lock.notifyAll() start
set lock.notifyAll() end
Set: 2
set lock.notifyAll() start
set lock.notifyAll() end
set while start = 2
set lock.wait() start
Get: 2, Mon Nov 17 09:11:55 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
Get: 1, Mon Nov 17 09:11:55 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
get while start = 0
get lock.wait() start
set lock.wait() end
set while end = 0
Set: 1
set lock.notifyAll() start
set lock.notifyAll() end
Set: 2
set lock.notifyAll() start
set lock.notifyAll() end
set while start = 2
set lock.wait() start
get lock.wait() end
get while end = 2
Get: 2, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
Get: 1, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
get while start = 0
get lock.wait() start
set lock.wait() end
set while end = 0
Set: 1
set lock.notifyAll() start
set lock.notifyAll() end
Set: 2
set lock.notifyAll() start
set lock.notifyAll() end
set while start = 2
set lock.wait() start
get lock.wait() end
get while end = 2
Get: 2, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
Get: 1, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
get while start = 0
get lock.wait() start
set lock.wait() end
set while end = 0
Set: 1
set lock.notifyAll() start
set lock.notifyAll() end
Set: 2
set lock.notifyAll() start
set lock.notifyAll() end
set while start = 2
set lock.wait() start
get lock.wait() end
get while end = 2
Get: 2, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
Get: 1, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
get while start = 0
get lock.wait() start
set lock.wait() end
set while end = 0
Set: 1
set lock.notifyAll() start
set lock.notifyAll() end
Set: 2
set lock.notifyAll() start
set lock.notifyAll() end
get lock.wait() end
get while end = 2
Get: 2, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end
Get: 1, Mon Nov 17 09:11:56 CST 2014
get lock.notifyAll() start
get lock.notifyAll() end


可见wait会让线程就在wait调用处等待,并且释放锁。

sleep只是等待,不会释放同步锁。



2.5 锁

锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。

一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。

不过,某些锁可能允许对共享资源并发访问,如ReadWriteLock的读取锁。


2.6 使用 Lock的Demo

先看看Lock怎么用, 先用synchronized实现一个打印队列

public class PrintQueue {    public PrintQueue() {}    public synchronized void printJob(Object doc) {        long duration = (long)(Math.random() * 10000);        System.out.println(Thread.currentThread().getName() + " :  start print " + (duration / 1000) +" secs");        try {            Thread.sleep(duration);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(Thread.currentThread().getName() + " :   print completed");    }}static class Job implements Runnable {private PrintQueue queue;public Job(PrintQueue queue) {this.queue = queue;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " run start");queue.printJob(new Object());System.out.println(Thread.currentThread().getName() + " run end");}}public static void main(String[] args) {PrintQueue queue = new PrintQueue();for (int i = 0; i < 10; i++) {new Thread(new Job(queue)).start();}}}

Thread-0 run start
Thread-1 run start
Thread-2 run start
Thread-0 :  start print 7 secs
Thread-3 run start
Thread-4 run start
Thread-5 run start
Thread-6 run start
Thread-7 run start
Thread-8 run start
Thread-9 run start
Thread-0 :   print completed
Thread-0 run end
Thread-9 :  start print 9 secs
Thread-9 :   print completed
Thread-9 run end
Thread-8 :  start print 1 secs
Thread-8 :   print completed
Thread-8 run end
Thread-7 :  start print 9 secs
Thread-7 :   print completed
Thread-7 run end
Thread-6 :  start print 8 secs
Thread-6 :   print completed
Thread-6 run end
Thread-5 :  start print 9 secs
Thread-5 :   print completed
Thread-5 run end
Thread-4 :  start print 1 secs
Thread-4 :   print completed
Thread-4 run end
Thread-3 :  start print 7 secs
Thread-3 :   print completed
Thread-3 run end
Thread-2 :  start print 9 secs
Thread-2 :   print completed
Thread-2 run end
Thread-1 :  start print 5 secs
Thread-1 :   print completed
Thread-1 run end

然后试试Lock同步的方式

public void printJob(Object doc) {queueLock.lock();System.out.println("queueLock.lock()");long duration = (long) (Math.random() * 10000 );System.out.println(Thread.currentThread().getName() + " :  start print " + (duration / 1000) + " secs");try {Thread.sleep(duration);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println("queueLock.unlock()");queueLock.unlock();}System.out.println(Thread.currentThread().getName() + " :   print completed");}


2.7 Lock的继承关系


入锁(ReentrantLock)是一种递归无阻塞的同步机制。

http://tenyears.iteye.com/blog/48750



Reentrant [rɪ'entrənt]



2.8 使用ReentrantReadWriteLock

这个类有两个锁一个是读锁,一个是写锁,读操作可以多线程同时访问,但是写操作时只允许一个线程进行。在写操作进行时,其它线程不能进行读操作。

public class PricesInfo {private int price1;private int price2;private ReadWriteLock rwLock = null;public PricesInfo() {price1 = 1;price2 = 2;rwLock = new ReentrantReadWriteLock();}public int getPrice1() {rwLock.readLock().lock();System.out.println(Thread.currentThread().getName()+ "get start price1");int temp = price1;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ "get end price1=" + price1);rwLock.readLock().unlock();return temp;}public int getPrice2() {rwLock.readLock().lock();System.out.println(Thread.currentThread().getName()+ "get start price2");int temp = price2;try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ "get end price2=" + price2);rwLock.readLock().unlock();return temp;}public void setPrices(int p1, int p2) {rwLock.writeLock().lock();System.out.println(Thread.currentThread().getName() + " set prices start");price1 = p1;price2 = p2;System.out.println(Thread.currentThread().getName() + " set price1=" + price1 + "  price2=" + price2);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " set prices end");rwLock.writeLock().unlock();}static class Reader implements Runnable {private PricesInfo prices;public Reader(PricesInfo prices) {this.prices = prices;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {prices.getPrice1();prices.getPrice2();}}}static class Writer implements Runnable {private PricesInfo prices;public Writer(PricesInfo prices) {this.prices = prices;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {this.prices.setPrices((int)(Math.random() * 10),(int)(Math.random() * 10));}try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {PricesInfo prices = new PricesInfo();for (int i = 0; i < 10; i++) {new Thread(new Reader(prices)).start();}for (int i = 0; i < 2; i++) {new Thread(new Writer(prices)).start();}}}
Thread-0get start price1
Thread-2get start price1
Thread-1get start price1
Thread-3get start price1
Thread-4get start price1
Thread-5get start price1
Thread-6get start price1
Thread-7get start price1
Thread-8get start price1
Thread-9get start price1
Thread-1get end price1=1
Thread-4get end price1=1
Thread-7get end price1=1
Thread-0get end price1=1
Thread-2get end price1=1
Thread-8get end price1=1
Thread-3get end price1=1
Thread-9get end price1=1
Thread-5get end price1=1
Thread-6get end price1=1
Thread-10 set prices start
Thread-10 set price1=3  price2=1
Thread-10 set prices end
Thread-11 set prices start
Thread-11 set price1=3  price2=2
Thread-11 set prices end
Thread-4get start price2
Thread-7get start price2
Thread-1get start price2
Thread-2get start price2
Thread-0get start price2
Thread-5get start price2
Thread-9get start price2
Thread-3get start price2
Thread-8get start price2
Thread-6get start price2
Thread-1get end price2=2
Thread-2get end price2=2
Thread-4get end price2=2
Thread-0get end price2=2
Thread-8get end price2=2
Thread-3get end price2=2
Thread-7get end price2=2
Thread-6get end price2=2
Thread-9get end price2=2
Thread-5get end price2=2
Thread-10 set prices start
Thread-10 set price1=9  price2=3
Thread-10 set prices end
Thread-10 set prices start
Thread-10 set price1=4  price2=9
Thread-10 set prices end
Thread-11 set prices start
Thread-11 set price1=9  price2=8
Thread-11 set prices end
Thread-1get start price1
Thread-2get start price1
Thread-4get start price1
Thread-0get start price1
Thread-8get start price1
Thread-3get start price1
Thread-6get start price1
Thread-7get start price1
Thread-5get start price1
Thread-9get start price1
Thread-4get end price1=9
Thread-9get end price1=9
Thread-8get end price1=9
Thread-2get end price1=9
Thread-0get end price1=9
Thread-7get end price1=9
Thread-6get end price1=9
Thread-5get end price1=9
Thread-3get end price1=9
Thread-1get end price1=9
Thread-11 set prices start
Thread-11 set price1=8  price2=2
Thread-11 set prices end
Thread-4get start price2
Thread-9get start price2
Thread-2get start price2
Thread-8get start price2
Thread-3get start price2
Thread-5get start price2
Thread-6get start price2
Thread-7get start price2
Thread-0get start price2

2.9 tryLock



2.10 修改锁的公平性 fairness

ReentrantLock和ReetrantReadWriteLock构造函数都含有一个boolean参数 fair,默认是false的,称为非公平模式

如果传入fair为true,则为公平模式,当很多线程等待锁(ReentrantLock或ReetrantReadWriteLock)时,锁将选择他们中等待时间最长的那个来访问临界区。

只针对lock和unlock方法,tryLock没有将线程休眠,fair不影响这个方法


public class PrintQueue {private final Lock queueLock = new ReentrantLock(true);  // 传入fair 为truepublic PrintQueue() {}public void printJob(Object doc) {queueLock.lock();System.out.println(Thread.currentThread().getName() + " run start queueLock.lock()");long duration = (long) (Math.random() * 10000 / 2);System.out.println(Thread.currentThread().getName() + " : start print " + (duration / 1000) + " secs");try {Thread.sleep(duration);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName() + "queueLock.unlock()");queueLock.unlock();}System.out.println(Thread.currentThread().getName() + " : print completed.");}static class Job implements Runnable {private PrintQueue queue;public Job(PrintQueue queue) {this.queue = queue;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " run start");queue.printJob(new Object());}}public static void main(String[] args) {PrintQueue queue = new PrintQueue();for (int i = 0; i < 10; i++) {new Thread(new Job(queue)).start();}}}


Thread-0 run start
Thread-2 run start
Thread-1 run start
Thread-0 run start queueLock.lock()
Thread-3 run start
Thread-0 : start print 0 secs
Thread-4 run start
Thread-5 run start
Thread-6 run start
Thread-7 run start
Thread-8 run start
Thread-9 run start
Thread-0queueLock.unlock()
Thread-0 : print completed.
Thread-2 run start queueLock.lock()
Thread-2 : start print 2 secs
Thread-2queueLock.unlock()
Thread-2 : print completed.
Thread-1 run start queueLock.lock()
Thread-1 : start print 4 secs
Thread-1queueLock.unlock()
Thread-1 : print completed.
Thread-3 run start queueLock.lock()
Thread-3 : start print 3 secs
Thread-3queueLock.unlock()
Thread-3 : print completed.
Thread-4 run start queueLock.lock()
Thread-4 : start print 3 secs
Thread-4queueLock.unlock()
Thread-5 run start queueLock.lock()
Thread-5 : start print 3 secs
Thread-4 : print completed.
Thread-5queueLock.unlock()
Thread-5 : print completed.
Thread-6 run start queueLock.lock()
Thread-6 : start print 4 secs
Thread-6queueLock.unlock()
Thread-6 : print completed.
Thread-7 run start queueLock.lock()
Thread-7 : start print 4 secs
Thread-7queueLock.unlock()
Thread-7 : print completed.
Thread-8 run start queueLock.lock()
Thread-8 : start print 4 secs
Thread-8queueLock.unlock()
Thread-8 : print completed.
Thread-9 run start queueLock.lock()
Thread-9 : start print 1 secs
Thread-9queueLock.unlock()
Thread-9 : print completed.


根据结果会发现,thread0获得锁后,先start的线程会先获得锁。


2.11 在锁中使用多条件

Condition接口提供了挂起线程和唤醒线程的机制。类似于wait notify 和notifyAll。

问题产生于,当Lock替代了synchronized时候 锁对象的wait()  notify()   notifyAll()用什么替代呢?引入了 Condition来进行锁对象(Lock或ReadWriteLock)等待和通知操作

不能重名 所以Condition对象 用await()  signal() signalAll()来处理代替Object对象的wait() notify() notifyAll()

Condition对象不能代替判断条件,条件仍需要再循环中判断 当不满足时候 继续await

    long awaitNanos(long nanosTimeout) throws InterruptedException;    boolean await(long time, TimeUnit unit) throws InterruptedException;         某个线程中断当前线程        其他某个线程调用了当前线程挂起的条件singal()和singnalAll()方法        制定等待时间过去     boolean awaitUntil(Date deadline) throws InterruptedException;某个线程中断当前线程其他某个线程调用了当前线程挂起的条件singal()和singnalAll()方法制定等待时间到了
 void awaitUninterruptibly();不可中断的,这线程将休眠直到其他某个线程调用了将它挂起条件的signal 或signalAll

Condition对象要通过Lock#newCondition()创建,才是相关锁的条件.而且Condition对象使用的代码,必须再相关联的Lock对象的lock()和unlock() 之间。

也可以将Condition对象和ReadLock和WriteLock一起使用,ReadLock和WriteLock也是继承自Lock接口

2.1.1 Lock和synchronized的对比

synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。

某些遍历并发访问的数据结果的算法要求使用 "hand-over-hand" 或 "chain locking":获取节点 A 的锁,然后再获取节点 B 的锁,然后释放 A 并获取 C,然后释放 B 并获取 D,依此类推。Lock 接口的实现允许锁在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁

随着灵活性的增加,也带来了更多的责任。不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。在大多数情况下,应该使用以下语句:

     Lock l = ...;      l.lock();     try {         // access the resource protected by this lock     } finally {         l.unlock();     } 
锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。



Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁尝试 (tryLock())、一个获取可中断锁的尝试 (lockInterruptibly()) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit))。

Lock 类还可以提供与隐式监视器锁完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录。

注意,Lock 实例只是普通的对象,其本身可以在 synchronized 语句中作为目标使用。获取 Lock 实例的监视器锁与调用该实例的任何 lock() 方法没有特别的关系。为了避免混淆,建议除了在其自身的实现中之外,决不要以这种方式使用Lock 实例。

  1. synchronized在离开块的时候自动放锁,其顺序没法控制,Lock需要自己控制放锁,可以按照任意顺序。
  2. Lock允许更复杂的临界区?使用更灵活
  3. Lock可以分离读写操作,允许多个线程读和只有一个线程写
  4. Lock接口又更好的性能
  5. Lock有tryLock方法如果,试图获取锁如果锁没有被占则获取之,如果锁被占则返回false继续执行下面的代码,不使线程等待。
  6. Lock使用的时候逻辑上清晰明确


tryLock要求开发人员出主意关注返回false时候,不要执行临界区代码。

ReentrantLock运行递归调用,将继续持有锁,lock方法立即返回


要十分小心使用锁,避免死锁


2.5 Thread状态 死锁理论



0 0
原创粉丝点击