Java Lock的使用

来源:互联网 发布:淘宝宝贝截图 编辑:程序博客网 时间:2024/05/16 08:57

首先扯点别的:记得以前在大学校里和同学一起打勾级,我怂恿我的队友杜仁建出牌,硬说我队友的对门周通要不了杜仁建的牌,原话是这样的“他(周通)要是能要了,我把牌吃了吐出来再吃”。结果他(周通)还是把我队友(杜仁建)给闷了。现在想想也是有意思。

今天记录一下Java中同步锁的使用。以后再慢慢理解。

首先看一下Lock这个接口,在java.util.concurrent.locks.Lock包下面,ReentrantLock:可重入锁,实现了Lock接口。

public interface Lock {    void lock();    void lockInterruptibly() throws InterruptedException;     boolean tryLock();     boolean tryLock(long time, TimeUnit unit) throws InterruptedException;     //释放锁    void unlock();    //返回一个和当前锁关联的Condition对象   Condition newCondition();

解释一下上面几个方法的作用

void lock();

获取锁,如果锁不可用,当前线程则不能被线程调度,并处于休眠状态,直到获得锁。

void lockInterruptibly() throws InterruptedException; 

获取锁,如果锁可用则立即返回。如果锁不可用,当前线程则不能被线程调度,并处于休眠状态,直到下面两种情况之一发生。
1. 当前线程获取到锁。
2. 当前线程被中断,抛出InterruptedException。

 boolean tryLock(); 

如果能够获取锁,立即返回true,否则立即返回false。使用tryLock( )惯用语法。

     Lock lock = ...;     if (lock.tryLock()) {       try {         // 获取成功操作        } finally {          lock.unlock();        }      } else {        //获取失败操作      }}
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  • 如果能够获取锁,方法立即返回true。
  • 如果锁不可用,当前线程会一直休眠等待直到下面三种情况中的一种情况发生。
    1. 当前线程成功获取了锁,返回true。
    2. 当前线程被其他线程中断,抛出InterruptedException异常。
    3. 等待时间超时,返回false。

ReentrantLock类中其他方法

//判断锁是否被线程持有 public boolean isLocked() {        return sync.isLocked();    }//判断当前锁是否是公平锁public final boolean isFair() {    return sync instanceof FairSync;}

具体的使用方法

lock()方法

public class Test {    private ArrayList<Integer> list = new ArrayList<>();    private Lock lock = new ReentrantLock();    public static void main(String[] args) {        Test test = new Test();        new Thread(new Runnable() {            @Override            public void run() {                test.insert(Thread.currentThread());            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                test.insert(Thread.currentThread());            }        }).start();    }    public void insert(Thread t) {        lock.lock();        try {            System.out.println(t.getName() + "得到了锁");            for (int i = 0; i < 5; i++) {                list.add(i);            }        } finally {            System.out.println(t.getName() + "释放了锁");            lock.unlock();        }    }}

输出结果

Thread-0得到了锁Thread-0释放了锁Thread-1得到了锁Thread-1释放了锁

tryLock()方法

public class Test {    private Lock lock = new ReentrantLock();    public static void main(String[] args) {        Test test = new Test();        new Thread(new Runnable() {            @Override            public void run() {                test.insert(Thread.currentThread());            }        }).start();        new Thread(new Runnable() {            @Override            public void run() {                test.insert(Thread.currentThread());            }        }).start();    }    public void insert(Thread t) {        if (lock.tryLock()) {            try {                System.out.println(t.getName() + "得到了锁");                try {                    //睡眠一会,但是不释放锁                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            } finally {                System.out.println(t.getName() + "释放了锁");                lock.unlock();            }        } else {            System.out.println(t.getName() + "获取锁失败");        }    }}

输出结果

Thread-0得到了锁Thread-1获取锁失败Thread-0释放了锁

lockInterruptibly()方法

public class Test {    private Lock lock = new ReentrantLock();    public static void main(String[] args) {        Test test = new Test();        MyThread thread1 = new MyThread(test);        MyThread thread2 = new MyThread(test);        thread1.start();        //延迟一会再启动线程2,否则可能出现线程2先被调度执行,那么线程2就不能被中断了        try {            Thread.sleep(2000);            System.out.println("启动线程" + thread2.getName());            thread2.start();        } catch (InterruptedException e) {            e.printStackTrace();        }        try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        thread2.interrupt();    }    public void insert(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;                //插入数据            }        } finally {            System.out.println(Thread.currentThread().getName() + "执行finally");            lock.unlock();            System.out.println(thread.getName() + "释放了锁");        }    }}class MyThread extends Thread {    private Test test = null;    public MyThread(Test test) {        this.test = test;    }    @Override    public void run() {        try {            test.insert(Thread.currentThread());        } catch (InterruptedException e) {            System.out.println(Thread.currentThread().getName() + "被中断");        }    }}

输出结果

Thread-0获得了锁Thread-1被中断

当thread1启动后,获得了锁。两秒后再启动thread2,然后thread2尝试获取锁,这时候,thread1正在持有锁,所以thread2处于等待状态。然后又过了两秒,调用thread2.interrupt()方法,中断thread2,lock.lockInterruptibly();就会抛出中断异常,在捕获的异常中输出Thread-1被中断。

ReadWriteLock:读写锁,也是一个接口,在java.util.concurrent.locks.ReadWriteLock包下,内部维持一对相关的锁。读锁,可以多个读者线程共用。写锁,互斥,同一时刻只能被一个线程使用。ReentrantReadWriteLock实现了这个接口。

public interface ReadWriteLock {    //返回用来读的锁    Lock readLock();    //返回用来写的锁    Lock writeLock();}
public class ReentrantReadWriteLock        implements ReadWriteLock, java.io.Serializable {    //内部类,提供读锁    private final ReentrantReadWriteLock.ReadLock readerLock;    //内部类,提供写锁    private final ReentrantReadWriteLock.WriteLock writerLock;    //...    //获取写锁     public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }     //获取读锁    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }    }

WriteLock的获取锁的方法,以最简单的lock( )方法为例

  /**  * 获取写锁  *如果读锁和写锁都没有被其他线程持有,则立即返回,并设置锁的计数为1。  *如果当前线程持有写锁,就把锁的计数加1,立即返回。  *如果写锁被其他线程持有,当前线程就不能被线程调度,并一直休眠,直到获取写锁(这时候写锁的计数为1)      */   public void lock() {        sync.acquire(1);    }     

ReadLock获取锁的方法,以最简单的lock( )方法为例

  //获取读锁  //如果写锁没有没其他线程持有,则立即返回。  //如果写锁被别的线程持有,则当前线程不能被线程调度,并一直休眠直到获取到读锁。   public void lock() {       sync.acquireShared(1);   }

多个线程同时使用读锁

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

输出结果会发现两个线程会交替输出,意思就是多个线程可以同时共用读锁。

一个线程使用读锁,一个线程使用写锁。

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

public class ReentrantReadWriteLockTest {    private ReadWriteLock rwl = new ReentrantReadWriteLock();    public static void main(String[] args) {        ReentrantReadWriteLockTest test = new ReentrantReadWriteLockTest();        new Thread() {            public void run() {                test.get(Thread.currentThread());            }        }.start();        new Thread() {            public void run() {                test.write(Thread.currentThread());            }        }.start();    }    private void get(Thread thread) {        rwl.readLock().lock();        try {            long start = System.currentTimeMillis();            while (System.currentTimeMillis() - start < 100) {                System.out.println(thread.getName() + "正在进行读操作");            }            System.out.println(thread.getName() + "读操作完毕");        } catch (Exception e) {            e.printStackTrace();        } finally {            rwl.readLock().unlock();        }    }    private void write(Thread thread) {        rwl.writeLock().lock();        try {            long start = System.currentTimeMillis();            while (System.currentTimeMillis() - start < 100) {                System.out.println(thread.getName() + "正在进行写操作");            }            System.out.println(thread.getName() + "写操作完毕");        } catch (Exception e) {            e.printStackTrace();        } finally {            rwl.writeLock().unlock();        }    }}

运行结果发现读和写是顺序执行的,也就是说,读锁和写锁是互斥的,同一时刻不能有一个线程使用读锁,一个线程使用写锁。

结尾:先记录下来,以后再慢慢体会,完善。

参考

【1】http://www.cnblogs.com/dolphin0520/p/3923167.html
【2】疯狂Java讲义

原创粉丝点击