多线程(五)线程同步(中)-Lock,Condition, ReadWriteLock

  线程同步(上)提到了七种同步方式,讲述了四种,剩下三种,大家可以继续深入了解下:
上篇博文地址:多线程(四) 线程同步(上)

<五>Lock 接口

Lock是java.util.concurrent.locks包下的接口,源码如下,注意相关接口:

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;

 *
 * @see ReentrantLock
 * @see Condition
 * @see ReadWriteLock
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Lock {

    void lock();    

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

需要注意的类:
ReentrantLock:
public class ReentrantLock implements Lock, java.io.Serializable {}

Condition :接口
public interface Condition{}

ReadWriteLock 接口
public interface ReadWriteLock{}

ReentrantReadWriteLock:
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable{}

Lock的特性:

  1).Lock不是Java语言内置的;

  2).synchronized是在JVM层面上实现的,如果代码执行出现异常,JVM会自动释放锁,但是Lock不行,要保证锁一定会被释放,就必须将unLock放到finally{}中(手动释放);

  3).在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetarntLock,但是在很激烈的情况下,synchronized的性能会下降几十倍;

  4).ReentrantLock增加了锁:

    a. void lock(); // 无条件的锁;

    b. void lockInterruptibly throws InterruptedException;//可中断的锁;

解释:使用ReentrantLock如果获取了锁立即返回,如果没有获取锁,当前线程处于休眠状态,直到获得锁或者当前线程可以被别的线程中断去做其他的事情;但是如果是synchronized的话,如果没有获取到锁,则会一直等待下去;

    c. boolean tryLock();//如果获取了锁立即返回true,如果别的线程正持有,立即返回false,不会等待;

    d. boolean tryLock(long timeout,TimeUnit unit);//如果获取了锁立即返回true,如果别的线程正持有锁,会等待参数给的时间,在等待的过程中,如果获取锁,则返回true,如果等待超时,返回false;

ReentrantLock及ReadWriteLock的使用方式,推荐大家看这篇博文:锁对象Lock-同步问题更完美的处理方式

  在这里要介绍的是Condition:

Condition的特性:

    1.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是需要与互斥锁/共享锁捆绑使用的。

    2.Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
    例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒”读线程”;当从缓冲区读出数据之后,唤醒”写线程”;并且当缓冲区满的时候,”写线程”需要等待;当缓冲区为空时,”读线程”需要等待。

     如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒”读线程”时,不可能通过notify()或notifyAll()明确的指定唤醒”读线程”,而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。 但是,通过Condition,就能明确的指定唤醒读线程。

示例代码:

package com.blog.spring.thread;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionTask {

    private final Lock lock = new ReentrantLock();

    private final Condition addCondition = lock.newCondition();

    private final Condition subCondition = lock.newCondition();

    private static int num = 0;
    private List<String> lists = new LinkedList<String>();

    public void add() {
        lock.lock();

        try {
            System.out.println("==============当前添加线程:"+Thread.currentThread().getName()+"================");
            while(lists.size() == 3) {//当集合已满,则"添加"线程等待
                System.out.println("当前集合大小为: " + lists.size()+"-->集合已满,添加线程"+Thread.currentThread().getName()+"开始等待,去唤醒减少线程");
                this.subCondition.signal();
                addCondition.await();
            }

            num++;
            String str="元素" + num;
            lists.add(str);
            System.out.println("添加 [" + str + "]");
            System.out.println("当前集合大小为: " + lists.size());
            System.out.println("当前添加线程为: " + Thread.currentThread().getName());
            System.out.println("==============去唤醒减少线程================");
            this.subCondition.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {//释放锁
            lock.unlock();
        }
    }


    public void sub() {
        lock.lock();

        try {
            System.out.println("==============当前减少线程:"+Thread.currentThread().getName()+"================");
            while(lists.size() == 0) {//当集合为空时,"减少"线程等待
                System.out.println("当前集合大小为: " + lists.size()+"-->集合为空,减少线程"+Thread.currentThread().getName()+"开始等待,去唤醒添加线程");
                addCondition.signal();
                subCondition.await();
            }

            num--;
            String str = lists.get(0);
            lists.remove(0);
            System.out.println("移除 [" + str + "]");
            System.out.println("当前集合大小为: " + lists.size());
            System.out.println("当前减少线程为: " + Thread.currentThread().getName());
            System.out.println("===============去唤醒添加线程===============");
            addCondition.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }


}



//测试类
package com.blog.spring.thread;

public class TestCondition {

    public static void main(String[] args) {

        final ConditionTask task = new ConditionTask();

        final ConditionTask task = new ConditionTask();

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    task.add();
                }
            },"添加线程"+i).start();
        }

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    task.sub();
                }
            },"减少线程"+i).start();
        }
}

运行结果:

==============当前添加线程:添加线程0================
添加 [元素1]
当前集合大小为: 1
当前添加线程为: 添加线程0
==============去唤醒减少线程================
==============当前添加线程:添加线程6================
添加 [元素2]
当前集合大小为: 2
当前添加线程为: 添加线程6
==============去唤醒减少线程================
==============当前添加线程:添加线程9================
添加 [元素3]
当前集合大小为: 3
当前添加线程为: 添加线程9
==============去唤醒减少线程================
==============当前添加线程:添加线程1================
当前集合大小为: 3-->集合已满,添加线程添加线程1开始等待,去唤醒减少线程
==============当前添加线程:添加线程2================
当前集合大小为: 3-->集合已满,添加线程添加线程2开始等待,去唤醒减少线程
==============当前添加线程:添加线程3================
当前集合大小为: 3-->集合已满,添加线程添加线程3开始等待,去唤醒减少线程
==============当前添加线程:添加线程4================
当前集合大小为: 3-->集合已满,添加线程添加线程4开始等待,去唤醒减少线程
==============当前添加线程:添加线程5================
当前集合大小为: 3-->集合已满,添加线程添加线程5开始等待,去唤醒减少线程
==============当前添加线程:添加线程7================
当前集合大小为: 3-->集合已满,添加线程添加线程7开始等待,去唤醒减少线程
==============当前减少线程:减少线程3================
移除 [元素1]
当前集合大小为: 2
当前减少线程为: 减少线程3
===============去唤醒添加线程===============
==============当前添加线程:添加线程8================
添加 [元素3]
当前集合大小为: 3
当前添加线程为: 添加线程8
==============去唤醒减少线程================
==============当前减少线程:减少线程0================
移除 [元素2]
当前集合大小为: 2
当前减少线程为: 减少线程0
===============去唤醒添加线程===============
==============当前减少线程:减少线程1================
移除 [元素3]
当前集合大小为: 1
当前减少线程为: 减少线程1
===============去唤醒添加线程===============
==============当前减少线程:减少线程2================
移除 [元素3]
当前集合大小为: 0
当前减少线程为: 减少线程2
===============去唤醒添加线程===============
==============当前减少线程:减少线程4================
当前集合大小为: 0-->集合为空,减少线程减少线程4开始等待,去唤醒添加线程
添加 [元素1]
当前集合大小为: 1
当前添加线程为: 添加线程1
==============去唤醒减少线程================
==============当前减少线程:减少线程5================
移除 [元素1]
当前集合大小为: 0
当前减少线程为: 减少线程5
===============去唤醒添加线程===============
==============当前减少线程:减少线程6================
当前集合大小为: 0-->集合为空,减少线程减少线程6开始等待,去唤醒添加线程
==============当前减少线程:减少线程7================
当前集合大小为: 0-->集合为空,减少线程减少线程7开始等待,去唤醒添加线程
==============当前减少线程:减少线程8================
当前集合大小为: 0-->集合为空,减少线程减少线程8开始等待,去唤醒添加线程
==============当前减少线程:减少线程9================
当前集合大小为: 0-->集合为空,减少线程减少线程9开始等待,去唤醒添加线程
添加 [元素1]
当前集合大小为: 1
当前添加线程为: 添加线程2
==============去唤醒减少线程================
添加 [元素2]
当前集合大小为: 2
当前添加线程为: 添加线程3
==============去唤醒减少线程================
添加 [元素3]
当前集合大小为: 3
当前添加线程为: 添加线程4
==============去唤醒减少线程================
当前集合大小为: 3-->集合已满,添加线程添加线程5开始等待,去唤醒减少线程
移除 [元素1]
当前集合大小为: 2
当前减少线程为: 减少线程4
===============去唤醒添加线程===============
添加 [元素3]
当前集合大小为: 3
当前添加线程为: 添加线程7
==============去唤醒减少线程================
移除 [元素2]
当前集合大小为: 2
当前减少线程为: 减少线程6
===============去唤醒添加线程===============
移除 [元素3]
当前集合大小为: 1
当前减少线程为: 减少线程7
===============去唤醒添加线程===============
移除 [元素3]
当前集合大小为: 0
当前减少线程为: 减少线程8
===============去唤醒添加线程===============
当前集合大小为: 0-->集合为空,减少线程减少线程9开始等待,去唤醒添加线程
添加 [元素1]
当前集合大小为: 1
当前添加线程为: 添加线程5
==============去唤醒减少线程================
移除 [元素1]
当前集合大小为: 0
当前减少线程为: 减少线程9
===============去唤醒添加线程===============

读写锁很重要,特在此保留一份:

package com.blog.spring.thread;

import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by hxf on 17/3/29.
 */
public class ReadWriteLockTest {

    public static void main(String[] args) {
        final Data data = new Data();
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                public void run() {
                    for (int j = 0; j < 5; j++) {
                        data.set(new Random().nextInt(30));
                    }
                }
            }).start();
        }
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                public void run() {
                    for (int j = 0; j < 5; j++) {
                        data.get();
                    }
                }
            }).start();
        }
    }

    static class Data {
        private int data;// 共享数据
        private ReadWriteLock rwl = new ReentrantReadWriteLock();
        public void set(int data) {
            rwl.writeLock().lock();// 取到写锁
            try {
                System.out.println(Thread.currentThread().getName() + "准备写入数据");
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.data = data;
                System.out.println(Thread.currentThread().getName() + "写入" + this.data);
            } finally {
                rwl.writeLock().unlock();// 释放写锁
            }
        }

        public void get() {
            rwl.readLock().lock();// 取到读锁
            try {
                System.out.println(Thread.currentThread().getName() + "准备读取数据");
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "读取" + this.data);
            } finally {
                rwl.readLock().unlock();// 释放读锁
            }
        }
    }
}

运行结果:

Thread-0准备写入数据
Thread-0写入9
Thread-0准备写入数据
Thread-0写入15
Thread-0准备写入数据
Thread-0写入14
Thread-1准备写入数据
Thread-1写入17
Thread-1准备写入数据
Thread-1写入27
Thread-1准备写入数据
Thread-1写入26
Thread-1准备写入数据
Thread-1写入22
Thread-1准备写入数据
Thread-1写入19
Thread-2准备写入数据
Thread-2写入18
Thread-2准备写入数据
Thread-2写入6
Thread-2准备写入数据
Thread-2写入16
Thread-2准备写入数据
Thread-2写入18
Thread-2准备写入数据
Thread-2写入29
Thread-3准备读取数据
Thread-4准备读取数据
Thread-5准备读取数据
Thread-5读取29
Thread-4读取29
Thread-3读取29
Thread-0准备写入数据
Thread-0写入24
Thread-0准备写入数据
Thread-0写入16
Thread-5准备读取数据
Thread-4准备读取数据
Thread-3准备读取数据
Thread-3读取16
Thread-4读取16
Thread-5读取16
Thread-4准备读取数据
Thread-3准备读取数据
Thread-5准备读取数据
Thread-4读取16
Thread-5读取16
Thread-3读取16
Thread-5准备读取数据
Thread-4准备读取数据
Thread-3准备读取数据
Thread-5读取16
Thread-3读取16
Thread-4读取16
Thread-3准备读取数据
Thread-5准备读取数据
Thread-4准备读取数据
Thread-5读取16
Thread-3读取16
Thread-4读取16
Process finished with exit code 0

思考:比较Condition与ReadWriteLock(ReentrantReadWriteLock是实现类)运行结果的区别?原因是啥?

Condition中的锁为:
private final Lock lock = new ReentrantLock();

结果显示:写入和写入互斥,读取和写入互斥,读取和读取互斥
不能并发执行,效率较低。Condition能够精确唤醒某种条件的线程(注意观察上面运行结果,如果拥有这种条件的线程数就一个,那么就能精确到线程,否则就是其中一个线程(随机唤醒)。),但不能并发执行-即所有线程之间都是互斥的。
  那么在这思考下Condition与Object(wait/notify,notifyAll)有什么区别?

ReadWriteLock中的锁为:
private ReadWriteLock rwl = new ReentrantReadWriteLock();

结果显示:虽然写入和写入互斥了,读取和写入也互斥了,但是读取和读取之间不互斥了,能够并发执行读取数据,相对效率较高,我想现在应该能很好的理解下面这句话了:

  在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetarntLock,但是在很激烈的情况下,synchronized的性能会下降几十倍;

参考文章:(http://www.cnblogs.com/Wanted-Tao/p/6378942.html);
http://blog.csdn.net/ghsau/article/details/7461369

上篇文章:多线程(四) 线程同步(上)-synchronized,volatile

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值