Java并发编程学习(四)—— Lock

一、ReentrantLock

1、Reentrant其实是ReEntrant,可重入的意思。先来个标准例子:

package com.amuro.studythread.chapter4_lock;

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample
{
    public static void main(String[] args)
    {
        A a = new A();

        for(int i = 0; i < 5; i ++)
        {
            Thread t = new Thread(new Runnable()
            {

                @Override
                public void run()
                {
                    a.a();
                }

            });
            t.setName("t" + (i + 1));
            t.start();
        }
    }

    static class A
    {
        ReentrantLock lock = new ReentrantLock();

        void a()
        {
            lock.lock();

            String tName = Thread.currentThread().getName();
            for(int i = 0; i < 10; i++)
            {
                System.out.println(tName + " : " + (i + 1));
            }

            b();

            lock.unlock();
        }

        void b()
        {
            lock.lock();

            System.out.println("tName b");

            lock.unlock();
        }
    }
}

程序输出:
t1 : 1
t1 : 2
t1 : 3
t1 : 4
t1 : 5
t1 : 6
t1 : 7
t1 : 8
t1 : 9
t1 : 10
t1 b
t2 : 1
t2 : 2
t2 : 3
t2 : 4
t2 : 5
t2 : 6
t2 : 7
t2 : 8
t2 : 9
t2 : 10
t2 b
t3 : 1
t3 : 2
t3 : 3
t3 : 4
t3 : 5
t3 : 6
t3 : 7
t3 : 8
t3 : 9
t3 : 10
t3 b
t4 : 1
t4 : 2
t4 : 3
t4 : 4
t4 : 5
t4 : 6
t4 : 7
t4 : 8
t4 : 9
t4 : 10
t4 b
t5 : 1
t5 : 2
t5 : 3
t5 : 4
t5 : 5
t5 : 6
t5 : 7
t5 : 8
t5 : 9
t5 : 10
t5 b
可以见这种情况下,lock的作用和synchronized的几乎一样,是一把可重入的锁。

2、condition/signal == wait/notify

package com.amuro.studythread.chapter4_lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample
{
    public static void main(String[] args) throws Exception
    {
        final Service service = new Service();

        Thread t = new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                service.await();
            }
        });
        t.start();

        Thread.sleep(3000);
        service.signal();
    }

    static class Service
    {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        void await()
        {
            try
            {
                lock.lock();
                System.out.println("await begin");
                condition.await();
                System.out.println("await end");
            }
            catch (Exception e) 
            {
            }
            finally
            {
                lock.unlock();
            }
        }

        void signal()
        {
            lock.lock();
            System.out.println("signal");
            condition.signal();
            lock.unlock();

        }
    }
}

程序输出:
await begin
signal
await end

package com.amuro.studythread.chapter4_lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample
{
    public static void main(String[] args) throws Exception
    {
        final Service service = new Service();

        Thread t1 = new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                service.await1();

            }
        });
        t1.start();

        Thread t2 = new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                service.await2();
            }
        });
        t2.start();

        Thread.sleep(3000);
        service.signal();
    }

    static class Service
    {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        void await1()
        {
            try
            {
                lock.lock();
                System.out.println("await1 begin");
                condition.await();
                System.out.println("await1 end");
            }
            catch (Exception e) 
            {
            }
            finally
            {
                lock.unlock();
            }
        }

        void await2()
        {
            try
            {
                lock.lock();
                System.out.println("await2 begin");
                condition.await();
                System.out.println("await2 end");
            }
            catch (Exception e) 
            {
            }
            finally
            {
                lock.unlock();
            }
        }

        void signal()
        {
            lock.lock();
            System.out.println("signal");
            condition.signalAll();
            lock.unlock();

        }
    }
}

程序输出:
await1 begin
await2 begin
signal
await1 end
await2 end

3、实现生产消费者模式

package com.amuro.studythread.chapter4_lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class SinglePC
{
    public static void main(String[] args)
    {
        P p = new P();
        C c = new C();

        new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                while(true)
                {
                    p.set();
                }
            }
        }).start();

        new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                while(true)
                {
                    c.get();
                }
            }
        }).start();


    }

    static String value = "";
    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    static class P
    {


        void set()
        {
            try
            {
                lock.lock();

                while(!"".equals(value))
                {
                    condition.await();
                }

                System.out.println("empty");
                value = "Double X";
                Thread.sleep(2000);
                condition.signal();

            }
            catch (Exception e) 
            {

            }
            finally
            {
                lock.unlock();
            }
        }
    }

    static class C
    {
        void get()
        {
            try
            {
                lock.lock();

                while("".equals(value))
                {
                    condition.await();
                }

                System.out.println(value);
                value = "";
                Thread.sleep(2000);
                condition.signal();

            }
            catch (Exception e) 
            {

            }
            finally
            {
                lock.unlock();
            }
        }
    }
}

4、公平锁与非公平锁
公平锁:线程获取锁的顺序遵循先到先得;
非公平锁:非先到先得,可能导致某些线程一直拿不到锁。
创建方法:

ReentrantLock lock = new ReentrantLock(true);//fair
ReentrantLock lock = new ReentrantLock(false);//unfair

5、lock的常用方法
1)int getHoldCount
当前线程保持此锁定的个数,也就是调用lock方法的次数。

2)int getQueueLength()
等待获取此lock的线程数,比如5个线程争夺一个lock,当1个线程获得lock后执行了sleep方法,此时调用getQueueLength方法返回的结果为4。

3)int getWaitQueueLength()
5个线程,每个线程都执行了同一个condition的await方法,则调用此方法返回5。

4)boolean hasQueueThread(thread)和bolean hasQueueThreads()
查询指定线程是否在等待获取锁;
查询是否有线程在等待获取锁。

5)boolean hasWaiters(condition)
查询是否有线程在用当前condition进行等待。

6)boolean isFair()
查询是否公平锁。

7)boolean isHeldByCurrentThread()和boolean isLocked()
查询当前线程是否获得了锁。
查询是否有线程了获得了锁。

8)void lockInterruptibly()
不太好解释,先看代码:

package com.amuro.studythread.chapter4_lock;

import java.util.concurrent.locks.ReentrantLock;

public class LockInterruptibly
{
    public static void main(String[] args) throws Exception
    {
        final Service s = new Service();
        Thread tA = new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                s.invoke();
            }
        });
        tA.start();
        Thread.sleep(500);

        Thread tB = new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                s.invoke();
            }
        });
        tB.start();

        tB.interrupt();
        System.out.println("main end");
    }

    static class Service
    {
        private ReentrantLock lock;

        public Service()
        {
            lock = new ReentrantLock();
        }

        void invoke()
        {
            try
            {
                lock.lock();
//              lock.lockInterruptibly();
                System.out.println(Thread.currentThread().getName() + " begin");

                for(int i = 0; i < Integer.MAX_VALUE / 10; i++)
                {
                    String str = new String();
                    Math.random();
                }   

                System.out.println(Thread.currentThread().getName() + " end");
            }
            catch (Exception e) 
            {
                System.out.println(Thread.currentThread().getName() + " goes into exception " + e.getMessage());
            }
            finally
            {
                if(lock.isHeldByCurrentThread())
                {
                    lock.unlock();
                }
            }
        }
    }
}

当使用lock.lock()时,打印结果为:
Thread-0 begin
main end
Thread-0 end
Thread-1 begin
Thread-1 end
当使用lock.lockInterruptibly时,打印结果为:
Thread-0 begin
main end
Thread-1 goes into exception null
Thread-0 end

由此可见,lockInterruptibly方法的意义是:如果线程未被中断(调用interrupt方法)时,可以正常获取锁,如果已经被中断则抛出异常。

9)boolean tryLock()
这个方法最大的好处就是发现拿不到锁的时候,可以进行其他处理。方法会直接返回false。

10)void awaitUninterruptibly()
正常情况下,在condition.await的时候调用线程的interrupt方法会导致抛出异常,但是如果使用awaitUninterruptibly方法,就不会抛出异常,程序会继续等待signal。

11)void awaitUtil(Date)

package com.amuro.studythread;

import java.util.Calendar;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Test
{
    public static void main(String[] args) throws Exception
    {
        final Test t = new Test();
        Thread th = new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                t.invoke();
            }
        });
        th.start();

        Thread.sleep(1000);
        t.signal();
    }

    ReentrantLock lock;
    Condition condition;

    public Test()
    {
        lock = new ReentrantLock();
        condition = lock.newCondition();
    }

    void invoke()
    {
        try
        {
            Calendar c = Calendar.getInstance();
            c.add(Calendar.SECOND, 3);
            lock.lock();
            System.out.println("begin");
            condition.awaitUntil(c.getTime());
            System.out.println("end");
        } 
        catch(Exception e)
        {
            System.out.println("exception: " + e.getMessage());
        }
        finally
        {
            lock.unlock();
        }
    }

    void signal()
    {
        try
        {
            lock.lock();
            condition.signal();
        }
        finally
        {
            lock.unlock();
        }
    }
}

线程会一直等待到传入的时间,但是可以被signal提前打断。

6、condition实现线程的顺序执行

package com.amuro.studythread.chapter4_lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class SequenceThreadInvoke
{
    static int go = 1;

    public static void main(String[] args)
    {
        final ReentrantLock lock = new ReentrantLock();
        final Condition condition = lock.newCondition();
        int length = 5;

        for(int i = 0; i < length; i++)
        {
            MyThread t = new MyThread(i + 1)
            {
                @Override
                public void run()
                {
                    while(true)
                    {
                        try
                        {
                            lock.lock();
                            while(go != getI())
                            {
                                condition.await();
                            }

                            System.out.println(getI());
                            if(go == length)
                            {
                                go = 1;
                            }
                            else
                            {
                                go++;
                            }
                            Thread.sleep(1000);
                            condition.signalAll();
                        }
                        catch (Exception e) 
                        {

                        }
                        finally
                        {
                            lock.unlock();
                        }
                    }
                }
            };
            t.start();
        }

    }

    static class MyThread extends Thread
    {
        int i;

        public MyThread(int i)
        {
            super();
            this.i = i;
        }

        public int getI()
        {
            return i;
        }
    }
}

二、ReentrantReadWriteLock

1、概念
ReentrantLock和synchronized都有天然的缺陷就是同一时间只能有一个线程获得锁,虽然保证了线程安全,但是效率极其低下。所以java又提供了读写锁,其中读锁是共享锁,在没有线程进行写入操作时,多个线程可以同时获得读锁;但是同一时刻只允许一个线程进行写操作。

2、读读共享,写写互斥

package com.amuro.studythread.chapter4_lock;

import java.util.Date;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class WRLockExample1
{
    public static void main(String[] args) throws Exception
    {
        final File file = new File();
        for(int i = 0; i < 10; i++)
        {
            new Thread(new Runnable()
            {

                @Override
                public void run()
                {
                    file.read();
                }
            }).start();
        }

        Thread.sleep(2000);

        for(int i = 0; i < 10; i++)
        {
            new Thread(new Runnable()
            {

                @Override
                public void run()
                {
                    file.write();
                }
            }).start();
        }
    }

    static class File
    {
        ReentrantReadWriteLock wrLock;

        public File()
        {
            wrLock = new ReentrantReadWriteLock();
        }

        void read()
        {
            try
            {
                wrLock.readLock().lock();
                System.out.println(
                        Thread.currentThread().getName() + 
                        " get read lock on " + 
                        new Date(System.currentTimeMillis()));
                Thread.sleep(1000);
            }
            catch (Exception e) 
            {

            }
            finally
            {
                wrLock.readLock().unlock();
            }
        }

        void write()
        {
            try
            {
                wrLock.writeLock().lock();
                System.out.println(
                        Thread.currentThread().getName() + 
                        " get write lock on " + 
                        new Date(System.currentTimeMillis()));
                Thread.sleep(1000);

            }
            catch(Exception e)
            {

            }
            finally
            {
                wrLock.writeLock().unlock();
            }
        }
    }

}

程序输出:
Thread-7 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-9 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-3 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-0 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-2 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-1 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-8 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-4 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-5 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-6 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-10 get write lock on Wed Jan 03 10:47:54 CST 2018
Thread-12 get write lock on Wed Jan 03 10:47:55 CST 2018
Thread-11 get write lock on Wed Jan 03 10:47:56 CST 2018
Thread-13 get write lock on Wed Jan 03 10:47:57 CST 2018
Thread-14 get write lock on Wed Jan 03 10:47:58 CST 2018
Thread-15 get write lock on Wed Jan 03 10:47:59 CST 2018
Thread-16 get write lock on Wed Jan 03 10:48:00 CST 2018
Thread-17 get write lock on Wed Jan 03 10:48:01 CST 2018
Thread-18 get write lock on Wed Jan 03 10:48:02 CST 2018
Thread-19 get write lock on Wed Jan 03 10:48:03 CST 2018
这个很好理解,不多说了;

3、读写互斥,写读互斥

package com.amuro.studythread.chapter4_lock;

import java.util.Date;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class WRLockExample2
{
    public static void main(String[] args) throws Exception
    {
        final File file = new File();
        new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                file.read();

            }
        }).start();

        new Thread(new Runnable()
        {

            @Override
            public void run()
            {
                file.write();
            }
        }).start();
    }

    static class File
    {
        ReentrantReadWriteLock wrLock;

        public File()
        {
            wrLock = new ReentrantReadWriteLock();
        }

        void read()
        {
            try
            {
                wrLock.readLock().lock();
                System.out.println(
                        Thread.currentThread().getName() + 
                        " get read lock on " + 
                        new Date(System.currentTimeMillis()));
                Thread.sleep(5000);
            }
            catch (Exception e) 
            {

            }
            finally
            {
                wrLock.readLock().unlock();
            }
        }

        void write()
        {
            try
            {
                wrLock.writeLock().lock();
                System.out.println(
                        Thread.currentThread().getName() + 
                        " get write lock on " + 
                        new Date(System.currentTimeMillis()));
                Thread.sleep(1000);

            }
            catch(Exception e)
            {

            }
            finally
            {
                wrLock.writeLock().unlock();
            }
        }
    }

}

程序输出:
Thread-0 get read lock on Wed Jan 03 11:05:47 CST 2018
Thread-1 get write lock on Wed Jan 03 11:05:52 CST 2018
可见当一个读写锁之间是互斥的,在读锁或者写锁没有unlock时,无法获得另一种锁。

你可能感兴趣的:(读书笔记)