zoukankan      html  css  js  c++  java
  • 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,对么?

    成灰之前,抓紧时间做点事!!
  • 相关阅读:
    2016-7-4工作总结
    2016-7第一周工作总结
    2016-6-30 工作总结
    2016-6-29 工作总结
    2016-6-28 工作总结
    基于软件开发对嵌入式开发的思考
    团队项目总结
    软件工程课程总结
    图描述之:流程图
    004-二叉树的遍历
  • 原文地址:https://www.cnblogs.com/jony-it/p/10833711.html
Copyright © 2011-2022 走看看