java同步:Lock

来源:互联网 发布:美工算平面设计吗 编辑:程序博客网 时间:2024/05/22 06:41

一、Lock与synchronized的比较

1、Lock不是java语言内置的,synchronized是java语言的关键字,因此是内置特性。Lock是一个接口,通过这个接口的实现类可以实现同步访问。
2、Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或synchronized代码块执行完成后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动去释放锁,就有可能导致死锁现象发生。

二、Java.util.concurrent.locks包下的两个接口

1、Lock接口:
Lock接口是一个通用接口,定义了对同步操作的基本方法
public interface Lock {    void lock();    void lockInterruptibly() throws InterruptedException;    boolean tryLock();    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;    void unlock(); }
Lock接口中:lock()、tryLock()、tryLock(long time,TimeUnit unit)、lockInterruptibly()是用来获取锁的。

unlock()是用来释放锁的。

四个获取锁方法的区别

1)、lock()方法:lock()方法是平常用的最多的方法,就是用来获取锁,如果锁已被其他线程获取,则进行等待。

注意:如果采用Lock方式,必须主动去释放锁,如果发生了异常,Lock方式是不会自动释放锁的。因此,一般来说,使用Lock必须在try{}catch(e){}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被释放,防止死锁的发生。

2)、tryLock()方法:tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,若获取失败(即锁已被其他线程获取),则返回false,也就是说,这个方法无论如何都会立即返回,在拿不到锁时不会一直在那儿等待。

3)、tryLock(Long time,TimeUnit unit)方法与tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待time指定的时间,在time时间之后,如果还拿不到锁,则返回false。如果一开始或在等待期间拿到了锁,则返回true。

4)、lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁时,则这个线程能够响应中断,即中断线程的等待状态。也就是说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假如此时线程A获取到了锁,线程B只有等待,那么线程B能够调用threadB.interrupt()方法能够中断线程B的等待过程。

注意:当一个线程获取了锁,是不会被interrupt方法中断的。

因此,通过lock.lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。

而用synchronized关键字进行修饰的话,当线程处于等待某个锁的状态下,是无法被中断啊的,只有一致等待下去。

2、ReadWriteLock接口
定义了如何获取读锁以及写锁的方法,也就是该接口将文件的读写操作分开,分成两个锁分配给线程,从而使多个线程可以同时进行读操作。
定义的基本方法如下:
public interface ReadWriteLock {    /**     * Returns the lock used for reading.     *     * @return the lock used for reading.     */    Lock readLock();     /**     * Returns the lock used for writing.     *     * @return the lock used for writing.     */    Lock writeLock();}

三、ReentrantLock类

直接使用Lock接口的话,我们需要实现很多的方法,不太方便,ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。
ReentrantLock,意思是“可重入锁”。
1、lock()方法的使用:
public class MyLockTest {private static ArrayList<Integer> arrayList = new ArrayList<Integer>();static Lock lock = new ReentrantLock(); // 注意这个地方public static <E> void main(String[] args) {new Thread() {public void run() {Thread thread = Thread.currentThread();lock.lock();try {System.out.println(thread.getName() + "得到了锁");for (int i = 0; i < 5; i++) {arrayList.add(i);}} catch (Exception e) {// TODO: handle exception} finally {System.out.println(thread.getName() + "释放了锁");lock.unlock();}};}.start();new Thread() {public void run() {Thread thread = Thread.currentThread();lock.lock();try {System.out.println(thread.getName() + "得到了锁");for (int i = 0; i < 5; i++) {arrayList.add(i);}} catch (Exception e) {// TODO: handle exception} finally {System.out.println(thread.getName() + "释放了锁");lock.unlock();}};}.start();}}
2、tryLock()方法的使用:
/** * 观察现象:一个线程获得锁后,另一个线程取不到锁,不会一直等待 * @author * */public class MyTryLock {private static ArrayList<Integer> arrayList = new ArrayList<Integer>();static Lock lock = new ReentrantLock(); // 注意这个地方public static void main(String[] args) {new Thread() {public void run() {Thread thread = Thread.currentThread();boolean tryLock = lock.tryLock();System.out.println(thread.getName()+" "+tryLock);if (tryLock) {try {System.out.println(thread.getName() + "得到了锁");for (int i = 0; i < 5; i++) {arrayList.add(i);}} catch (Exception e) {// TODO: handle exception} finally {System.out.println(thread.getName() + "释放了锁");lock.unlock();}}};}.start();new Thread() {public void run() {Thread thread = Thread.currentThread();boolean tryLock = lock.tryLock();System.out.println(thread.getName()+" "+tryLock);if (tryLock) {try {System.out.println(thread.getName() + "得到了锁");for (int i = 0; i < 5; i++) {arrayList.add(i);}} catch (Exception e) {// TODO: handle exception} finally {System.out.println(thread.getName() + "释放了锁");lock.unlock();}}};}.start();}}
3、lockInterruptibly()响应中断的方法的使用:

/** * 观察现象:如果thread-0得到了锁,阻塞。。。thread-1尝试获取锁,如果拿不到,则可以被中断等待 * @author * */public class MyInterruptibly { private Lock lock = new ReentrantLock();       public static void main(String[] args)  {    MyInterruptibly test = new MyInterruptibly();        MyThread thread0 = new MyThread(test);        MyThread thread1 = new MyThread(test);        thread0.start();        thread1.start();                 try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        thread1.interrupt();        System.out.println("=====================");    }           public void insert(Thread thread) throws InterruptedException{        lock.lockInterruptibly();   //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出        try {              System.out.println(thread.getName()+"得到了锁");            long startTime = System.currentTimeMillis();            for(    ;     ;) {                if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)                    break;                //插入数据            }        }        finally {            System.out.println(Thread.currentThread().getName()+"执行finally");            lock.unlock();            System.out.println(thread.getName()+"释放了锁");        }      }} class MyThread extends Thread {    private MyInterruptibly test = null;    public MyThread(MyInterruptibly test) {        this.test = test;    }    @Override    public void run() {                 try {            test.insert(Thread.currentThread());        } catch (Exception e) {            System.out.println(Thread.currentThread().getName()+"被中断");        }    }}

四、ReentrantReadWriteLock类:

ReentrantReadWriteLock中提供了很多丰富的方法,不过最主要的有两个方法:
1)、readLock(),用来获取读锁
2)、writeLock(),用来获取写锁

假如有多个线程进行读操作时,synchronized与ReentrantReadWriteLock方式比较:

1、synchronized方式:

** * 一个线程又要读又要写,用synchronize来实现的话,读写操作都只能锁住后一个线程一个线程地进行 * @author * */public class MySynchronizedReadWrite {        public static void main(String[] args)  {        final MySynchronizedReadWrite test = new MySynchronizedReadWrite();                 new Thread(){            public void run() {                test.get(Thread.currentThread());            };        }.start();                 new Thread(){            public void run() {                test.get(Thread.currentThread());            };        }.start();             }           public synchronized void get(Thread thread) {        long start = System.currentTimeMillis();        int i=0;        while(System.currentTimeMillis() - start <= 1) {        i++;        if(i%4==0){            System.out.println(thread.getName()+"正在进行写操作");        }else {        System.out.println(thread.getName()+"正在进行读操作");}        }        System.out.println(thread.getName()+"读写操作完毕");    }}
运行结果:

。。。Thread-0正在进行读操作Thread-0正在进行读操作Thread-0正在进行读操作Thread-0正在进行写操作Thread-0读写操作完毕Thread-1正在进行读操作Thread-1正在进行读操作Thread-1正在进行读操作。。。
结果分析:不管运行多少次,都是同样的结果,不管是读操作还是写操作,当一个线程获取锁并执行,另一个线程只有等待,当前线程执行完成后,等待线程才能执行,

2、ReentrantReadWriteLock方式:

/** * 使用读写锁,可以实现读写分离锁定,读操作并发进行,写操作锁定单个线程 *  * 如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。 * 如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。 * @author * */public class MyReentrantReadWriteLock { private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();         public static void main(String[] args)  {        final MyReentrantReadWriteLock test = new MyReentrantReadWriteLock();                 new Thread(){            public void run() {                test.get(Thread.currentThread());                test.write(Thread.currentThread());            };        }.start();                 new Thread(){            public void run() {                test.get(Thread.currentThread());                test.write(Thread.currentThread());            };        }.start();             }          /**     * 读操作,用读锁来锁定     * @param thread     */    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();        }    }    /**     * 写操作,用写锁来锁定     * @param thread     */    public void write(Thread thread) {        rwl.writeLock().lock();;        try {            long start = System.currentTimeMillis();                         while(System.currentTimeMillis() - start <= 1) {                System.out.println(thread.getName()+"正在进行写操作");            }            System.out.println(thread.getName()+"写操作完毕");        } finally {            rwl.writeLock().unlock();        }    }}
运行结果:

Thread-0正在进行读操作Thread-1正在进行读操作Thread-0正在进行读操作Thread-1正在进行读操作Thread-0正在进行读操作。。。Thread-1读操作完毕Thread-0读操作完毕Thread-0正在进行写操作Thread-0正在进行写操作Thread-0正在进行写操作Thread-0正在进行写操作Thread-0正在进行写操作。。。Thread-0写操作完毕Thread-1正在进行写操作Thread-1正在进行写操作Thread-1正在进行写操作Thread-1正在进行写操作。。。Thread-1写操作完毕
结果分析:

当两个线程获取的都是读锁的时候,读操作并发的进行,不会有线程进行等待,

当其中有线程需要获取写锁时,必须等待其他线程操作完成。

当其中有线程已经获取了写锁,其他线程必须等待当前线程写操作完成。

注意:

如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。

如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或读锁,则申请的线程会一直等待释放写锁。

五、Lock与synchronized选择

1、Lock是一个接口,而synchronized是java中的关键字,synchronized是内置的语言实现。
2、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象的发生;而Lock在发生异常时,如果没有主动通过unLock去释放锁,则可能造成死锁现象,因此使用Lock时,需要在finally块中释放锁。
3、Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。
4、通过Lock可以知道有没有成功获取锁,而synchronized无法办到。
5、Lock可以提高多线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized的,所以说,具体使用时要根据实际情况来选择。










0 0
原创粉丝点击