可重入锁 ReentranLock 和 Condition

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq1169091731/article/details/83067127

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

猜你喜欢

转载自blog.csdn.net/qq1169091731/article/details/83067127