JUC高级多线程_01:基础知识回顾

我是 ABin-阿斌:写一生代码,创一世佳话,筑一揽芳华。 如果小伙伴们觉得我的文章有点 feel ,那就点个赞再走哦。
在这里插入图片描述

1 . JUC 是什么

  • java.util.concurrent 在并发编程中使用的工具类
  • JUC三大包: 并发包、并发原子包、并发lock包

2 . 进程/线程是什么

1. 进程

  • 简单的说,就是后台运行的一个程序就是一共进程,是和操作系统有关。
  • 例子: 写论文的时候,用 word 写论文,同时用 QQ 音乐放音乐,同时用 QQ 聊天,多个进程

2. 线程

  • **线程:**通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。
  • 例子: word 如没有保存,停电关机,再通电后打开 word 可以恢复之前未保存的文档,word 也会检查你的拼写,两个线程:容灾备份,语法检查

3 . 并发/并行是什么

1. 并发

  • 同一时刻多个线程在访问同一个资源,多个线程对一个点
  • 例子:手机发布,限量抢购;春运抢票; 电商秒杀…

2. 并行

  • 并行: 多项工作一起执行,之后再汇总
  • 例子: 泡方便面,电水壶烧水,一边撕调料倒入桶中

4 . wait / sleep 区别

  • wait : 放开手去睡,放开手里的锁
  • sleep : 握紧手去睡,醒了手里还有锁

5 . 线程六大状态

  1. NEW:(新建)
  2. RUNNABLE:(准备就绪)
  3. BLOCKED:(阻塞)
  4. WAITING:(不见不散,会一直等待,直到来了)
  5. TIMED_WAITING:(过时不候,只会等待一段时间 时间过了还不来,就走了)
  6. TERMINATED:(终结)

6 . 复习售票问题

1. 题目 :三个售票员 卖出 30张票

2. 口诀:线程 操作 资源类

3. 模板1.0

  • 小标号代表对口诀的解释的顺序
public class JUC01_saleTicket01 {
    //main 一切程序的入口
    public static void main(String[] args) {
        //1.2 初始化资源类
        Ticket01 ticket = new Ticket01();
        //1.3 线程:有三个售票员就新建三个线程,三个线程操作同一个资源类
        //实际使用 Thread(Runnable target, String name) 新建进程
        //使用匿名内部类,直接 new 一个 Runnable 接口对象
        //进程 1
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 30; i++){
                    ticket.sale();
                }
            }
        },"Thread_A").start();
        //进程2
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 30; i++){
                    ticket.sale();
                }
            }
        },"Thread_B").start();
        //进程3
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 30; i++){
                    ticket.sale();
                }
            }
        },"Thread_C").start();
    }
}
//1.1 资源类
//需要做到高内聚,就要把对自己的操作的方法、把对外提供的功能放在自己身上
class Ticket01 {
    private int number = 30;
    //资源类,自带了对外提供的功能
    // 使用 synchronized 进行加锁
    public synchronized void sale() {
		//售票的业务逻辑
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + " 卖出第:" + (number--) + "票,还剩下:" + number + "张");
        }
    }
}


3. 模板2.0最终

  • 因为 使用匿名内部类创建线程代码过于冗长,所以需要 使用 Lambda 表达式 进行优化
  • 因为 使用 synchronized 加锁,会对整个方法加锁,粒度相对较大,所以 使用 lock 接口及其实现类 ReentrantLock(可重入锁),来优化
  • 优化后,如下:
public class JUC01_saleTicket01 {
    //main 一切程序的入口
    public static void main(String[] args) {
        //1.2 初始化资源类
        Ticket01 ticket = new Ticket01();
        //1.3 线程:有三个售票员就新建三个线程,三个线程操作同一个资源类
    	//使用  Lambda表达式 优化,不过为了思路不乱、清晰
        // 最好先用匿名内部类写个例子,再变换
        new Thread(()->{for (int i = 1; i <= 30; i++) ticket.sale();},"Thread_A").start();
        new Thread(()->{for (int i = 1; i <= 30; i++) ticket.sale();},"Thread_B").start();
        new Thread(()->{for (int i = 1; i <= 30; i++) ticket.sale();},"Thread_C").start();
    }
}
//1.1 资源类
//需要做到高内聚,就要把对自己的操作的方法、把对外提供的功能放在自己身上
class Ticket01 {
    private int number = 30;
    //不使用 synchronized ,因为加上  synchronized 的方法总所有代码都会被加锁
    //改用 lock 接口及其实现类 ReentrantLock(可重入锁),来优化
    private Lock lock = new ReentrantLock();
    //资源类,自带了对外提供的功能
    public void sale() {
        // lock 模块
        // 上锁
        lock.lock();
        try {
            //售票的业务逻辑
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + " 卖出第:" + (number--) + "票,还剩下:" + number + "张");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 开锁
            lock.unlock();
        }

    }
}


7 . 上述模板中的知识点补充

1. 关于 thread.start() 的问题

  • 执行这句话,线程不会马上启动,只是代表这个线程进入就绪态
  • 只有当 操作系统 和 CPU 底层调用到线程中的 run 方法才表示线程启动

2. Lambda表达式

  • Lambda表达式,只能在实例化接口的时候,并且该接口中有且仅有一个方法的时候使用

  • 使用时的口诀: 拷贝小括号,写死右箭头,落地大括号

  • 以上述模板为例

// 起初初始化
new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 30; i++){
                    ticket.sale();
                }
            }
},"Thread_A").start();

// 使用 Lambda表达式
new Thread(()->{for (int i = 1; i <= 30; i++) ticket.sale();},"Thread_A").start();

  • 说明:
    1. 拷贝小括号: 照抄 run 方法后的小括号里所有的东西,(如果有参数也照抄,数据类型可以省略)
      例子: public int sum(int x,int y){} =》(x,y)-> {}
    2. 写死右箭头
    3. 落地大括号: 就把原来方法 {} 中的照抄
  1. @FunctionalInterface: 函数式接口
  • 只有接口中有且仅有一个方法时,才可以使用 Lambda 表达式 进行实例化
  • 如果一个接口中只有一个方法,在 java 底层会自动加上 @FunctionalInterface 注解,标明这是一个函数接口
  • 如果一个接口被 @FunctionalInterface 标注,那么该接口中只能有一个方法,多写会报错
  1. default
  • java8 以后支持在 函数接口 中默认实现该接口中的方法
  • 例子:
    接口
// 接口
@FunctionalInterface
interface FUN{
    public int sum(int X, int Y);
    default int sum2(int X, int Y){
        return X + Y;
    }
}

调用(需要使用该接口的实例对象才能调用)

//调用
FUN f = new FUN();
f.sum2(1,2);

  • 一个 函数接口 中可以有多个 默认的实现方法(default )
  1. static 静态方法
  • 函数接口 里的静态方法必须时实现了的方法
  • 例子:
    接口
// 接口
@FunctionalInterface
interface FUN{
    public int sum(int X, int Y);
    public static int sum2(int X, int Y){
        return X + Y;
    }
}

调用(只能使用该接口名才能调用)

//调用
FUN.sum2(1,2);

  • 一个 函数接口 可以有多个静态方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值