JUC Lock锁与线程通信

JUC Lock锁

多线程编程步骤

运用了高内聚低耦合的方法

  • 创建资源类,在资源类创建属性和方法
  • 创建多个线程,调用资源类的操作方法

Lock接口实例

synchronized锁上锁和解锁的过程由jvm隐式的完成

Lock接口可以手动的实现上锁和解锁的过程

ReentrantLock(可重入锁)

ReentrantLock是lock接口的一个实现类,使用可重入锁的方法对线程的同步来进行控制

可重入锁:进入上锁退出解锁 主要的区别在于需要手动的上锁和解锁。

package com.xiaoxu.lock;

import java.util.concurrent.locks.ReentrantLock;
/**
 * 可重入锁
 * @author chenruxu
 */

//创建资源类
class LTicket{

    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    private int number=30;
    //卖票的方法
    public void sale(){
        //上锁
        lock.lock();
        try {
            //判读是否有票可卖
            if(number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(+number--)+"张票");

            }
        }finally {
            //解锁
            lock.unlock();
        }
    }

}
public class LSaleTicket {
    public static void main(String[] args) {
        LTicket ticket = new LTicket();
        //lamber表达式

        /**
         * AA线程
         */
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"AA").start();
        /**
         * BB线程
         */
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"BB").start();
        /**
         * CC线程
         */
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"CC").start();
    }
}

通过可重入锁多线程买票的并发问题,对卖票的过程加锁来解决了线程之间的不安全问题。

其中Lock是一个接口,在发生异常的时候不会和synchronized一样自动的释放锁,可能会引起死锁现象。

线程之间的通信

多线程编程的步骤2

  • 创建资源类

  • 在资源类中操作方法 判读 干活 通知。

  • 创建多个线程,调用资源类操作方法,完成过程。

  • 防止虚假唤醒的问题

实例一:a(add)线程 +1 b线程 -1

变量值为1的时候加1 0的时候a线程等待。

实现方式synchronized关键字实现和lock锁的实现方式

synchronized实现方式

package com.xiaoxu.synchroniz;
/**
 * synchronized关键字实现
 *
 */
//创建资源类
class Share{
    //初始值
    private int number = 0;

    //+1的方法
    public synchronized void incr() throws InterruptedException {
        if (number!=0){ //判断number是否为0 等待
            this.wait();
        }
        //如果number的值是0就加1操作
        number++;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他的线程(个人理解就是解锁)
        this.notifyAll();
    }

    /**
     * -1的方法
     */
    public synchronized void decr() throws InterruptedException {
        if(number!=1){
            this.wait();
        }
        //实现步骤
        number--;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他的线程
        this.notifyAll();
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        //创建多个线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjw4cGRq-1638446376730)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211202155350256.png)]

线程之间的通信虚假唤醒问题

产生虚假唤醒问题的代码

package com.xiaoxu.synchroniz;
/**
 * synchronized关键字实现
 *
 */
//创建资源类
class Share{
    //初始值
    private int number = 0;

    //+1的方法
    public synchronized void incr() throws InterruptedException {
        if (number!=0){ //判断number是否为0 等待
            this.wait();
        }
        //如果number的值是0就加1操作
        number++;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他的线程(个人理解就是解锁)
        this.notifyAll();
    }

    /**
     * -1的方法
     */
    public synchronized void decr() throws InterruptedException {
        if(number!=1){
            this.wait();
        }
        //实现步骤
        number--;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他的线程
        this.notifyAll();
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        //创建多个线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    e.printStakTrace();
                }
            }
        },"DD").start();

    }
}

产生虚假唤醒的原因:线程的虚假唤醒的问题。wait()方法释放锁之后,如果该线程继续抢到cpu的资源之后,会在wait()语句这里唤醒,if语句的判断会失效。解决的方法是将if语句换成对应的while语句解决虚假唤醒的问题。

下面是将原来的资源类中对资源进行操作的方法中进行锁条件判断语句中的if替换为while之后解决的过程。

package com.xiaoxu.synchroniz;
/**
 * synchronized关键字实现
 *
 */
//创建资源类
class Share{
    //初始值
    private int number = 0;

    //+1的方法
    public synchronized void incr() throws InterruptedException {
        while (number!=0){ //判断number是否为0 等待
            this.wait();
        }
        //如果number的值是0就加1操作
        number++;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他的线程(个人理解就是解锁)
        this.notifyAll();
    }

    /**
     * -1的方法
     */
    public synchronized void decr() throws InterruptedException {
        while (number!=1){
            this.wait();
        }
        //实现步骤
        number--;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他的线程
        this.notifyAll();
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        //创建多个线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr(); //+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"DD").start();

    }
}

Lock接口实现

package com.xiaoxu.lock;

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

/**
 * lock接口实现
 */

//创建资源类
class Share{
    private int number = 0;

    //创建lock
    private Lock lock= new ReentrantLock();
    private Condition condition=lock.newCondition();
    //+1
    public void incr() throws InterruptedException {
        //加锁
        lock.lock();
        try {
            //判断
            while (number!=0){
                condition.await();
            }
            //操作
            number++;
            System.out.println(Thread.currentThread().getName()+"::"+number);
            //唤醒
            condition.notify();
        }finally {
            //解锁
            lock.unlock();
        }
    }

    //-1
    public void decr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            while (number!=1){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"::"+number);
            condition.notify();
        }finally {
            //解锁
            lock.unlock();
        }

    }

}

public class ThreadDemo2 {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

    }
}

线程的定制化通信

应用场景:

启动三个线程按照如下的要求进行

AA打印5次 BB打印10次 CC打印15次共执行10轮

使我们线程之间的顺序按照指定的顺序进行。思路为三个线程设置三个标志位。标志位1打印A5次 修改标志位为2通知B过程以此类推。最后一个CC线程的标志位改完1。完成操作。下面对代码进行实现。

package com.xiaoxu.lock;

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

//第一步 创建资源类
class ShareResource{
    //定义标志位
    private int flag = 1; //1 AA 2BB 3CC
    //创建lock锁
    private Lock lock = new ReentrantLock();

    //创建三个condition
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    //打印5次
    public void print5(int loop) throws InterruptedException {
        lock.lock();
        try{
            //判断
            while (flag!=1){
                c1.await();
            }
            //干活
            for (int i = 1; i <=5; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+"轮数"+loop);
            }
            //通知
            flag = 2;
            c2.signal();
        }finally {
            //释放锁
            lock.unlock();
        }
    }

    public void print10(int loop) throws InterruptedException {
        //上锁操作
        lock.lock();
        try {
            while (flag!=2){
                c2.await();
            }
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+"轮数"+loop);
            }
            //修改标志位 进行通知Cc线程
            flag = 3;
            c3.signal();
        }finally {
            //解锁
            lock.unlock();
        }
    }


    public void print15(int loop) throws InterruptedException {
        //上锁操作
        lock.lock();
        try {
            while (flag!=3){
                c3.await();
            }
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+"轮数"+loop);
            }
            //修改标志位 进行通知Cc线程
            flag = 1;
            c1.signal();
        }finally {
            //解锁
            lock.unlock();
        }
    }
}

public class ThreadDemo3 {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        //创建多线程
        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    shareResource.print5(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    shareResource.print10(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();

        new Thread(()->{
            for (int i = 1; i <= 10; i++) {
                try {
                    shareResource.print15(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总有人会抬头看见月亮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值