JAVA 多线程(8):synchronized 的同伙lock

Lock:
lock对象功能类似synchronized ,但是更加方便,或者说有更多的功能。

 

实现类:

1.ReentrantLock

2.ReentrantReadWriteLock : 读写互斥,比1功能再多一点

 

一、ReentrantLock

首先回顾一下synchronized:

public void test(){
        synchronized (this){
            System.out.println(Thread.currentThread().getName()+"begin");
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args){
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };

        Thread t = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t.start();
        t2.start();
    }

输出:

 

 结果是同步的。

看一下lock 实现:

private Lock lock = new ReentrantLock();

    public void test() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

输出结果与上面的一致。

相比synchronized个人感觉更清晰,至少不用去找大括号了。

 

关于lock 的wait与notify:

lock 同样有等待/通知机制,只不过调用的方法名称不同,并且细粒度更强。

在调用时,需要声明一个条件对象 condition(跟wait一样,必须在lock范围内),如下;

private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void test() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition.await();
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void test2() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition.signal();
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                testRLock.test2();
            }
        };
        Thread t = new Thread(runnable);
        Thread t2 = new Thread(runnable2);
        t.start();
        try {
            Thread.sleep(2000);
            t2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

输出:

 

wait 对应的是 await,notify 对应的是signal,notifyAll 对应的是signalAll。

 

区别:使用condition 的好处是可以控制通知部分线程,因为conditon 可以创建多个的缘故。

 如下:

private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private Condition condition2 = lock.newCondition();

    public void test() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition.await();
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void test3() {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + "begin");
        try {
            condition2.await();
            System.out.println(Thread.currentThread().getName() + "end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void test2() {
        lock.lock();
        condition.signal();
        System.out.println(Thread.currentThread().getName() + "唤醒了等待1");
        lock.unlock();
    }

    public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                testRLock.test2();
            }
        };
        Runnable runnable3 = new Runnable() {
            @Override
            public void run() {
                testRLock.test3();
            }
        };
        Thread t = new Thread(runnable, "我是等待1");
        Thread t3 = new Thread(runnable3, "我是等待2");
        Thread t2 = new Thread(runnable2, "我来唤醒1");
        t.start();
        t3.start();
        try {
            Thread.sleep(3000);
            t2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

输出:

 

 例子中增加了一个conditon 实例,并且conditon2的等待是没有人通知他的。由此实现的是通知部分等待线程。

 

 其他功能:

1.getHoldCount() :获取当前锁定的个数,在unlock之前保持此锁的个数。

2.getQueueLength():获取等待锁释放的线程数

3.getWaitQueyeLenth():获取调用await的conditon的线程数,如果有5个线程同时调用了同一个codition的await,且都没有调用signal 释放锁,那么此值为5.

 

4.hasQueueThread():查询某个线程是否获得了锁 ,返回值为布尔类型

5.hasQueueThreads():查询是否有线程在等待调用锁,返回值为布尔

6.hasWaiters():查询是否有线程在等待conditon 通知

 

7.isFair():判断是否为公平锁。 公平锁-》是否为先进先出。在创建锁实例时是否设定。

 

8.isHeldByCurrentThread():当前线程是否保持了该锁。

9.isLocked():查询lock是否被任意线程保持。

 

还有其他的一些用法,在此不一个个敲字了。。。

 

二、ReentrantReadWriteLock

从名字看出,它持有2把锁,一把read锁不互斥,也就是共享锁,另一把write锁互斥。

读读共享:

 

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void test() {
        lock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.readLock().unlock();
    }

    public void test2() {
        lock.writeLock().lock();
        System.out.println(Thread.currentThread().getName() + System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.writeLock().unlock();
    }

    public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Thread t = new Thread(runnable, "A");
        Thread t2 = new Thread(runnable, "B");
        t.start();
        t2.start();
    }

 

 

 

 

 

 

输出:

 

写写互斥:

 

 把main主线程调用中runnable run 方法改为调用test2

输出:

 

 由结果看出,写锁必须等待,也就是同步操作。

 

读写互斥(凡是有写入操作的,都会等待)

main 方法修改为:

public static void main(String[] args) {
        TestRLock testRLock = new TestRLock();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testRLock.test();
            }
        };
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                testRLock.test2();
            }
        };
        Thread t = new Thread(runnable, "A");
        Thread t2 = new Thread(runnable2, "B");
        t.start();
        t2.start();
    }

 

输出:

 

 总结:

其实如果没有太需要的话使用synchronized 就足够了,但是如果追求控制欲的同学可以使用lock,对么?

 

posted @ 2019-05-08 18:49  李鹏飞ONLINE  阅读(242)  评论(0编辑  收藏  举报