可重入锁 ReentranLock 和 Condition

ReentrantLock 可重用锁

1. 同一个线程未释放锁之前获取同一把锁

一个线程在获取了锁之后,再次去获取了同一个锁,这时候仅仅是把使用计数 +1

package lock9_method.holdCount;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by lenovo on 2018/5/10.
 */
public class Service {
    public ReentrantLock lock = new ReentrantLock();

    public void method() {
        try {
            lock.lock();
            System.out.println(lock.getHoldCount());
            Thread.sleep(1000);
            method2();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void method2(){
        lock.lock();
        System.out.println(lock.getHoldCount());
        lock.unlock();

    }
}
package lock9_method.holdCount;

/**
 * Created by lenovo on 2018/5/10.
 */
public class Main {
    public static void main(String[] args) {
        Service service = new Service();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<10;i++){
                    service.method();
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();

        Thread countThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(2000);
                        //当前线程拥有 该锁的计数,在这调用肯定为 0
                        System.out.println("当前锁被线程a 持有计数:"+service.lock.getHoldCount());
                    } catch (InterruptedException e) {

                        e.printStackTrace();
                    }
                }
            }
        });
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        countThread.start();


    }
}


输出:

1
2
1
2
1
2
1
2
1
当前锁被线程a 持有计数:0
2
1
2
1
当前锁被线程a 持有计数:0

2. 使用 ReentrantLock 、Condition演示简单等待唤醒机制

package lock2;

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

/**
 * Created by lenovo on 2018/5/3.
 */
public class MyService206 {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void waitMethod() {
        try {
            lock.lock();
            for (int i = 0; i < 3; i++) {
                System.out.println("wait:" + i);
            }
            condition.await();
            for (int i = 4; i < 6; i++) {
                System.out.println("wait:" + i);
            }
            lock.unlock();
            System.out.println("wait方法中锁被释放");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public void signalMethod() {
        lock.lock();
        for (int i = 0; i < 3; i++) {
            System.out.println("notify:" + i);
        }
        condition.signal();
        for (int i = 4; i < 6; i++) {
            System.out.println("notify:" + i);
        }
        lock.unlock();
        System.out.println("notify 方法中锁被释放");

    }

}
package lock2;

/**
 * Created by lenovo on 2018/5/3.
 */
public class ThreadA extends Thread {
    private MyService206 myService206;

    public ThreadA(MyService206 myService206) {
        this.myService206 = myService206;
    }

    @Override
    public void run() {
        myService206.waitMethod();
    }
}
package lock2;

/**
 * Created by lenovo on 2018/5/3.
 */
public class ThreadB extends Thread {
    private MyService206 myService206;

    public ThreadB(MyService206 myService206) {
        this.myService206 = myService206;
    }

    @Override
    public void run() {
        myService206.signalMethod();
    }
}
package lock2;

/**
 * 使用 Condition实现简单等待唤醒机制
 * Created by lenovo on 2018/5/3.
 */
public class Run206 {
    public static void main(String[] args) {
        MyService206 myService206 = new MyService206();
        ThreadA threadA = new ThreadA(myService206);
        threadA.start();

        ThreadB threadB = new ThreadB(myService206);
        threadB.start();

    }
}


输出:

wait:0
wait:1
wait:2
notify:0
notify:1
notify:2
notify:4
notify:5
wait:4
wait:5
wait方法中锁被释放
notify 方法中锁被释放

3. 同一把锁、多个 Condition 实现唤醒 指定等待线程

package lock4;

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

/**
 * Created by lenovo on 2018/5/3.
 */
public class MyService {

    ReentrantLock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();

    public void awaitA() {
        try {
            lock.lock();
            for (int i = 0; i < 2; i++) {
                System.out.println("awaitA: " + i);
            }
            conditionA.await();
            for (int i = 3; i < 5; i++) {
                System.out.println("awaitA: " + i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println("awaitA unlock");
        }

    }

    public void awaitB() {
        try {
            lock.lock();
            for (int i = 0; i < 2; i++) {
                System.out.println("awaitB: " + i);
            }
            conditionB.await();
            for (int i = 3; i < 5; i++) {
                System.out.println("awaitB: " + i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            System.out.println("awaitB unlock");
        }

    }

    public void signalAll_A() {
        try {
            lock.lock();
            conditionA.signalAll();
            System.out.println("线程 A 被唤醒");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void signalAll_B() {
        try {
            lock.lock();
            conditionB.signalAll();
            System.out.println("线程 B 被唤醒");

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

        }

    }
}
package lock4;

/**
 * Created by lenovo on 2018/5/3.
 */
public class ThreadA extends Thread {
    private MyService myService;

    public ThreadA(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.awaitA();
    }
}
package lock4;

/**
 * Created by lenovo on 2018/5/3.
 */
public class ThreadB extends Thread {
    private MyService myService;

    public ThreadB(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.awaitB();
    }
}
package lock4;

/**
 * Created by lenovo on 2018/5/3.
 * 使用多个 Condition 实现通知指定 等待线程
 *
 */
public class Run212 {
    public static void main(String[] args) {
        MyService myService = new MyService();
        ThreadA threadA = new ThreadA(myService);
        ThreadB threadB = new ThreadB(myService);

        threadA.start();
        threadB.start();
        try {
            Thread.sleep(4000);
            myService.signalAll_A();

            Thread.sleep(3000);
            myService.signalAll_B();

        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

输出

awaitB: 0
awaitB: 1
awaitA: 0
awaitA: 1
线程 A 被唤醒
awaitA: 3
awaitA: 4
awaitA unlock
线程 B 被唤醒
awaitB: 3
awaitB: 4
awaitB unlock

4. 使用 ReentrantLock 和 Condition实现生产者消费者模式

4.1 缓存中只支持一个值

package lock5_producer;

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

/**
 * Created by lenovo on 2018/4/23.
 */
public class MyService213 {
    private ReentrantLock reentrantLock = new ReentrantLock();
    private Condition condition = reentrantLock.newCondition();

    private boolean hasValue = false;

    /**
     * 生产者
     */
    public void set() {
        try {
            reentrantLock.lock();

            while (hasValue == true) {
                // 有值,生产者线程等待
                condition.await();
            }
            // 无值
            System.out.println("生产者在生产...");
            hasValue = true;

            condition.signal();  // 生产之后 唤醒 消费者
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

    /**
     * 消费者
     */

    public void get() {
        try {
            reentrantLock.lock();
            while (hasValue == false) {
                // 无值,消费者在等待
                condition.await();
            }
            System.out.println("消费者在消费");
            hasValue = false;
            condition.signal();  // 消费完之后唤醒生产者

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

}
package lock5_producer;

/**
 * Created by lenovo on 2018/4/23.
 */
public class MyThreadA extends Thread {
    private MyService213 myService213;

    public MyThreadA(MyService213 myService213) {
        this.myService213 = myService213;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            myService213.get();
        }
    }


}
package lock5_producer;

/**
 * Created by lenovo on 2018/4/23.
 */
public class MyThreadB extends Thread {
    private MyService213 myService213;

    public MyThreadB(MyService213 myService213){
        this.myService213 = myService213;
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            myService213.set();
        }
    }
}
package lock5_producer;

/**
 * Created by lenovo on 2018/4/23.
 * ReentrantLock 、Condition 实现生产者消费者模式(缓存中数据最大值为 1)
 */
public class Run213 {
    public static void main(String[] args) {
        MyService213 myService213 = new MyService213();
        MyThreadA myThreadA = new MyThreadA(myService213);
        myThreadA.start();

        MyThreadB myThreadB = new MyThreadB(myService213);
        myThreadB.start();
    }
}


输出:

生产者在生产...
消费者在消费
生产者在生产...
消费者在消费
生产者在生产...
消费者在消费
生产者在生产...
消费者在消费

4.2 缓存支持多个值

package lock6_consumer;

import com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader;

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

/**
 * Created by lenovo on 2018/4/23.
 */
public class MyService215 {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private int valueCount = 0;
    private int bufferMaxSize = 20; 

    /**
     * 生产者
     */
    public void set() {
        try {
            lock.lock();
            while (valueCount == bufferMaxSize) {
                // 如果缓存已满,释放锁等待
                condition.await();
            }
            // 唤醒等待线程
            condition.signalAll();

            // 缓存未满
            valueCount++; // 生产一个 添加到缓存中
            System.out.println("生产者生产,缓存中的数据共" + valueCount + "个");

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

    /**
     * 消费者
     */

    public void get() {
        try {
            lock.lock();
            while (valueCount == 0) {
                condition.await();
            }
            condition.signalAll();
            // 消费
            valueCount--;
            System.out.println("消费者消费,缓存中的数据剩余:" + valueCount + " 个");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

/**
 * Created by lenovo on 2018/4/23.
 */
public class MyThreadA extends Thread {
    private MyService215 myService213;

    public MyThreadA(MyService215 myService213) {
        this.myService213 = myService213;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            myService213.get();
        }
    }
}
public class MyThreadB extends Thread {
    private MyService215 myService213;

    public MyThreadB(MyService215 myService213){
        this.myService213 = myService213;
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            myService213.set();
        }
    }
}
public class Run215 {
    public static void main(String[] args) {
        MyService215 myService213 = new MyService215();


        MyThreadB consumer = new MyThreadB(myService213);
        consumer.start();

        MyThreadA producer = new MyThreadA(myService213);
        producer.start();

    }
}

输出:

生产者生产,缓存中的数据共1个
生产者生产,缓存中的数据共2个
生产者生产,缓存中的数据共3个
生产者生产,缓存中的数据共4个
消费者消费,缓存中的数据剩余:3 个
消费者消费,缓存中的数据剩余:2 个
消费者消费,缓存中的数据剩余:1 个
消费者消费,缓存中的数据剩余:0 个
生产者生产,缓存中的数据共1个
生产者生产,缓存中的数据共2个
生产者生产,缓存中的数据共3个
生产者生产,缓存中的数据共4个
生产者生产,缓存中的数据共5个
....

4.3 使用不同的 Condition 唤醒同类线程

在 4.2 中使用 condition.signalAll() 是为了唤醒所有处于WAITING的线程,避免出现假死现象,但如果唤醒的是同类线程, 则又会进入WAITING状态,浪费资源

解决方案: 一类线程使用一个 Condition对象

package lock5_producer;

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

/**
 * Created by lenovo on 2018/5/11.
 */
public class Service {
    private ReentrantLock lock = new ReentrantLock();
    private Condition producerCondition = lock.newCondition();
    private Condition consumerCondition = lock.newCondition();

    private int bufferMaxSize = 2;
    private int nowCount = 0;

    public void produce() {
        try {
            lock.lock();
            while (nowCount == bufferMaxSize) {
                System.out.println("生产者: "+Thread.currentThread().getName()+" 等待");
                producerCondition.await();
                System.out.println("生产者: "+Thread.currentThread().getName()+" 被唤醒");
            }
            nowCount++;
            consumerCondition.signal();
            System.out.println("生产者: "+Thread.currentThread().getName()+"生产产品,总计" + nowCount);

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

    public void consumer() {
        try {
            lock.lock();
            while (nowCount == 0) {
                System.out.println("消费者: "+Thread.currentThread().getName()+"等待");
                consumerCondition.await();
                System.out.println("消费者: "+Thread.currentThread().getName()+"被唤醒");
            }
            nowCount--;
            System.out.println("消费者: "+Thread.currentThread().getName()+" 消费产品,剩余 " + nowCount);
            producerCondition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        }   finally {
            lock.unlock();
        }
    }
}
package lock5_producer;

/**
 * Created by lenovo on 2018/5/11.
 */
public class Main {
    public static void main(String[] args) {
        Service service = new Service();
        Runnable proRun = new Runnable() {
            @Override
            public void run() {
                service.produce();
            }
        };
        Runnable conRun = new Runnable() {
            @Override
            public void run() {
                service.consumer();
            }
        };

        Thread[] consumer = new Thread[30];
        Thread[] producer = new Thread[30];
        for (int i = 0; i < 30; i++) {
            consumer[i] = new Thread(conRun);
            consumer[i].setName("consumer" + i);
            producer[i] = new Thread(proRun);
            producer[i].setName("producer" + i);
        }
        for (int i = 0; i < 30; i++) {
            consumer[i].start();
            producer[i].start();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}



输出

生产者: producer25 等待
生产者: producer2 等待
消费者: consumer23被唤醒
消费者: consumer23 消费产品,剩余 1
消费者: consumer29 消费产品,剩余 0
消费者: consumer28等待
生产者: producer27生产产品,总计1
消费者: consumer27 消费产品,剩余 0
消费者: consumer25等待
生产者: producer24生产产品,总计1
消费者: consumer24 消费产品,剩余 0
生产者: producer25 被唤醒
生产者: producer25生产产品,总计1
生产者: producer2 被唤醒
生产者: producer2生产产品,总计2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容