01Lock锁

1.进程及线程

进程:一个程序的集合,一个进程包含多个线程,至少一个。
线程:对于Java而言,Thread 、Runnable、Callable

Java是开启不了线程的,需要调用本地C++的方法启动硬件来开启线程。

2.并发及并行

并发:多线程操作同一个资源(CPU单核)
并行:多条任务一起行走(CPU多核)

获取CPU核数
在这里插入图片描述
并发编程本质:要充分利用CPU资源处理任务

3.线程状态
  1. NEW:新生状态
  2. BLOCKED:阻塞状态
  3. WAITING:等待状态
  4. TIME_WAITING:超时等待状态
  5. TERMINTED:终止状态

sleep与wait的区别

  1. sleep来自Thread类,wait来自Object类
  2. wait会释放锁,sleep不会释放锁
  3. wait必须在同步代码块中使用,sleep可以在任何地方使用
  4. wait不需要捕获异常,sleep需要捕获异常
Synchronized锁与Lock锁

以多线程卖票为例:

public class Test {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
    }
}
class Ticket{
    private int num=40;
    public void sale(){
        if (num>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"的票,剩余票数"+num);
        }
    }
}

在这里插入图片描述
不难看出,多线程卖票在不加锁的情况下操作统一资源很容易出错。
解决1:传统解决方法是在同步代码中加入Sychronized锁:
在这里插入图片描述
运行如下:
在这里插入图片描述
解决2:加Lock锁
在这里插入图片描述
新建Lock锁,先加锁,业务代码用try catch包裹捕获异常,执行完之后在解锁,保证数据一致性。

    public void sale(){
        Lock lock=new ReentrantLock();
        try {
            lock.lock();
        }catch (Exception e){
            if (num>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"的票,剩余票数"+num);
            }
        }
        finally {
            lock.unlock();
        }

执行结果:
在这里插入图片描述
sychronized锁与Lock锁的区别

  1. sychronized是一个关键字,lock是一个类
  2. sychronized无法获取锁的状态,lock通过加锁和解锁可以
  3. sychronized会自动释放锁,lock需要手动解锁,更加灵活,如果不释放锁,会造成死锁
  4. sychronized锁阻塞时,另一个线程会一直等待下去,lock锁通过 lock.tryLock()来尝试获取锁,不会一直等待
  5. sychronized可重入锁 、不可中断、非公平。lock可重入锁,可以判断,通过加入参数设置是否可公平。
  6. sychronized适合锁少量的同步代码,lock适合大量的锁同步代码
提供者与消费者

sychronized版

package com.wang.JUC;

/**
 * @author 
 * @Date 2022/7/9
 * @apiNote
 */
public class Procom {
    public static void main(String[] args) {
        Provider provider=new Provider();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.incr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者A").start();
        
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.decr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者B").start();

    }
/*
等待 业务 唤醒
 */
static class Provider{
        private int num=0;
        public synchronized void incr() throws InterruptedException {
            if (num!=0){
                this.wait();
            }
            num++;
            System.out.println("正在执行加操作"+num);
            this.notifyAll();
        }
        public synchronized void decr() throws InterruptedException {
            if (num==0){
                this.wait();
            }
            num--;
            System.out.println("正在执行减操作"+num);
            this.notifyAll();
        }

    }
}

执行:在这里插入图片描述

若四个线程同时进行,两个线程进行加操作,两个线程进行减操作,则会出现虚假唤醒问题,即线程可以唤醒,而不会被通知。中断或者超时:
在这里插入图片描述
解决方法:等待应该出现在循环当中,即用while来替代if进行判断
在这里插入图片描述
执行解决:在这里插入图片描述
JUC版本:


/**
 * @author wangchangjun
 * @Date 2022/7/9
 * @apiNote
 */
public class Procom {
    public static void main(String[] args) {
        Provider provider=new Provider();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.incr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.decr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.incr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者C").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.decr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者D").start();

    }
/*
等待 业务 唤醒
 */
static class Provider{
        private int num=0;
        Lock lock=new ReentrantLock();
        Condition condition= lock.newCondition();
        public void incr() throws InterruptedException {
            lock.lock();//上锁
            try {
                while (num!=0){
                   condition.await();//代替等待
                }
                num++;
                System.out.println("正在执行加操作"+num);
                condition.signalAll();//代替唤醒
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//解锁
            }
        }
        public  void decr() throws InterruptedException {
            lock.lock();

            try {
                while (num==0){
                    condition.await();
                }
                num--;
                System.out.println("正在执行减操作"+num);
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }


        }

    }
}

执行:
在这里插入图片描述
缺陷:线程执行不是按顺序唤醒,需要通过Condition来优化


/**
 * @author 
 * @Date 2022/7/9
 * @apiNote
 */
public class Procom {
    public static void main(String[] args) {
        Provider provider=new Provider();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.incr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.decr();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    provider.decr1();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        },"消费者C").start();
//
//        new Thread(()->{
//            for (int i = 0; i < 10; i++) {
//                try {
//                    provider.decr();
//                }catch (Exception e){
//                    e.printStackTrace();
//                }
//            }
//        },"消费者D").start();

    }
/*
等待 业务 唤醒
 */
static class Provider{
        private int num=1;
        Lock lock=new ReentrantLock();
        Condition conditionA= lock.newCondition();//分别监控
        Condition conditionB= lock.newCondition();
        Condition conditionC= lock.newCondition();
        public  void incr() throws InterruptedException {
            lock.lock();
            try {
                while (num!=1){
                    conditionA.await();
                }
                num=2;
                System.out.println(Thread.currentThread().getName()+"正在执行加操作"+num);
                conditionB.signal();//A执行完唤醒指定线程B
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public  void decr() throws InterruptedException {
            lock.lock();

            try {
                while (num!=2){
                    conditionB.await();
                }
                num=3;
                System.out.println(Thread.currentThread().getName()+"正在执行减操作"+num);
                conditionC.signal();//B执行完唤醒指定线程C
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }


        }

    public  void decr1() throws InterruptedException {
        lock.lock();

        try {
            while (num!=3){
                conditionC.await();
            }
            num=1;
            System.out.println(Thread.currentThread().getName()+"正在执行减操作"+num);
            conditionA.signal();//C线程执行完再次唤醒线程A
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }


    }

    }
}

执行
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值