01_JUC概述

2023-05-16

1. JUC是什么?

在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类。此包包括了几个小的、已标准化的可扩展框架,并提供一些功能实用的类,没有这些类,一些功能会很难实现或实现起来冗长乏味。

参照JDK文档:

2. 进程和线程

进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。

线程多了好还是少好?

合理最好: 一般和CPU核心数差不错最好
1、如果程序执行的是耗时操作 线程多一些好
2、如果程序执行的是cpu计算型操作,线程越少越好   cpu调度线程时 切换上下文也需要时间

生活实例: 

  • ​使用QQ,查看进程一定有一个QQ.exe的进程,我可以用qq和A文字聊天,和B视频聊天,给C传文件,给D发一段语言,QQ支持录入信息的搜索。
  • ​大四的时候写论文,用word写论文,同时用QQ音乐放音乐,同时用QQ聊天,多个进程。
  • word如没有保存,停电关机,再通电后打开word可以恢复之前未保存的文档,word也会检查你的拼写,两个线程:容灾备份,语法检查

3. 并行和并发

串行:

资源:服务器中的一个文件 或者是某个接口

并发:同一时刻多个线程在访问同一个资源,多个线程对一个点

​例子:小米13pro今天上午10点,限量抢购

春运抢票

电商秒杀...

并行:多项工作一起执行,之后再汇总

例子:泡方便面,电水壶烧水,一边撕调料倒入桶中

4. wait/sleep的区别

功能都是当前线程暂停,有什么区别?

wait:放开手去睡,放开手里的锁

sleep:握紧手去睡,醒了手里还有锁

wait是Object的方法,sleep是thread的方法

wait从等待时所在的行继续向后执行

5. 创建线程回顾

创建线程常用两种方式:

  • 继承Thread:java是单继承,资源宝贵,要用接口方式
  • 实现Runnable接口

5.1 继承Thread抽象类

public class MyThread extends Thread
new MyThread().start();

5.2 实现Runnable接口的方式

① 新建类实现runnable接口。这种方法会新增类,有更好的方法

class MyRunnable implements Runnable//新建类实现runnable接口
new Thread(new MyRunnable(), name).start() // 使用Rannable实现类创建进程,name是线程名

② 匿名内部类

new Thread(new Runnable() {
    @Override
    public void run() {
 		// 调用资源方法,完成业务逻辑
    }
}, "your thread name").start();

6. lambda表达式

之前说了Runnable接口的两种实现方式,其实还有第三种:

1. 创建类实现Runnable接口
2. 编写匿名内部类实现Runnable接口
3. lambda表达式:这种方法代码更简洁精炼

new Thread(() -> {

}, "your thread name").start();

6.1 什么是lambda

Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:

  • 左侧:指定了 Lambda 表达式需要的所有参数
  • 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能

6.2案例

在一个方法中调用接口中的方法:传统写法

interface Foo {

    public int add(int x, int y);
}

public class LambdaDemo {
    public static void main(String[] args) {
        Foo foo = new Foo() {
            @Override
            public int add(int x, int y) {
                return x + y;
            }
        };
        System.out.println(foo.add(10, 20));
    }
}

接下来,要用lambda表达式改造。其实是改造main方法

public static void main(String[] args) {

    Foo foo = (int x, int y)->{
        return x + y;
    };
    System.out.println(foo.add(10, 20));
}

改造口诀:拷贝小括号(),写死右箭头->,落地大括号{...}

思考:如果Foo接口有多个方法,还能使用lambda表达式吗?

6.4 函数式接口

lambda表达式,必须是函数式接口,必须只有一个方法,如果接口只有一个方法java默认它为函数式接口。为了正确使用Lambda表达式,需要给接口加个注解:@FunctionalInterface。如有两个方法,立刻报错。

Runnable接口为什么可以用lambda表达式?

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

发现Runnable接口上有一个注解:@FunctionalInterface

并且该接口只有一个方法:run()方法

其实,函数式接口必须只有一个方法,这个描述并不准确,它还允许有default方法和静态方法。

例如,在Foo接口中,又添加了sub方法和mul方法:

interface Foo {

    public int add(int x, int y); // 抽象方法

    default int sub(int x, int y){ // default方法
        return x - y;
    }
    public static int mul(int x, int y){ // 静态方法
        return x * y;
    }
}

public class LambdaDemo {

    public static void main(String[] args) {

        Foo foo = (int x, int y)->{  // lambda表达式实现抽象方法
            return x + y;
        };
        System.out.println(foo.add(10, 20)); // 调用抽象方法
        System.out.println(foo.sub(30, 15)); // 调用default方法
        System.out.println(Foo.mul(10, 50)); // 通过Foo调用静态方法
    }
}

6.4 小结

lambda表达式实现接口的前提是:有且只有一个抽象方法,可以选择@FunctionalInterface注解增强函数式接口定义


改造口诀​:拷贝小括号(形参列表),写死右箭头 ->,落地大括号 {方法实现}

7. synchronized回顾

(1)多线程编程模板上

1. 线程 操作 资源类
2. 高内聚低耦合

(2)实现步骤

1. 创建资源类
2. 资源类里创建同步方法、同步代码块
3. 多线程调用

(4)例子:卖票程序

class Ticket {

    private Integer number = 20;

    public synchronized void sale(){
        if (number <= 0) {
            System.out.println("票已售罄!!!");
            return;
        }
        try {
            System.out.println(Thread.currentThread().getName() + "开始买票,当前票数:" + number);
            Thread.sleep(200);
            System.out.println(Thread.currentThread().getName() + "买票结束,剩余票数:" + --number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 在main方法中创建多线程方法,测试卖票业务
public class SaleTicket {

    public static void main(String[] args) {

        Ticket ticket = new Ticket();

        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        }, "AAA").start();

        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        }, "BBB").start();

        new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        }, "CCC").start();

    }
}

8. synchronized的8锁问题

看下面这段儿代码,回答后面的8个问题:

class Phone {

    public synchronized void sendSMS() throws Exception {
        //TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

    public void getHello() {
        System.out.println("------getHello");
    }

}

public class Lock_8 {

    public static void main(String[] args) throws Exception {

        Phone phone = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            try {
                phone.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "AA").start();

        Thread.sleep(100);

        new Thread(() -> {
            try {
                phone.sendEmail();
                //phone.getHello();
                //phone2.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "BB").start();
    }
}

多线程的8个问题:

1. 标准访问,先打印短信还是邮件
2. 停4秒在短信方法内,先打印短信还是邮件
3. 普通的hello方法,是先打短信还是hello
4. 现在有两部手机,先打印短信还是邮件
5. 两个静态同步方法,1部手机,先打印短信还是邮件
6. 两个静态同步方法,2部手机,先打印短信还是邮件
7. 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
8. 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件

答案:

1. 标准访问,先打印短信还是邮件: 先短信
2. 停4秒在短信方法内,先打印短信还是邮件:先短信(两个方法锁的对象都是调用方法的phone)
3. 普通的hello方法,是先打短信还是hello:先hello(hello方法未加锁)
4. 现在有两部手机,先打印短信还是邮件:先邮件(两个线程各自用各自的锁)
5. 两个静态同步方法,1部手机,先打印短信还是邮件:先短信
6. 两个静态同步方法,2部手机,先打印短信还是邮件:先短信(静态方法调用锁定的对象和谁调用无关,锁的一定是类对象)
7. 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件:先邮件
8. 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件:先邮件
一个线程会等待另一个线程释放锁以后执行,那么它俩争抢的锁一定是同一个 

总结:

synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式:

1. 对于普通同步方法,锁是当前实例对象。
2. 对于静态同步方法,锁是当前类的Class对象。
3. 对于同步方法块,锁是Synchonized括号里配置的对象

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

也就是说:

  • 如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁;可是不同实例对象的非静态同步方法因为用的是不同对象的锁,所以毋须等待其他实例对象的非静态同步方法释放锁,就可以获取自己的锁。
  • 所有的静态同步方法用的是同一把锁——类对象本身。不管是不是同一个实例对象,只要是一个类的对象,一旦一个静态同步方法获取锁之后,其他对象的静态同步方法,都必须等待该方法释放锁之后,才能获取锁。
  • 而静态同步方法(Class对象锁)与非静态同步方法(实例对象锁)之间是不会有竞态条件的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

01_JUC概述 的相关文章

  • 01_JUC概述

    1 JUC是什么 xff1f 在 Java 5 0 提供了 java util concurrent 简称JUC 包 xff0c 在此包中增加了在并发编程中很常用的工具类 此包包括了几个小的 已标准化的可扩展框架 xff0c 并提供一些功能
  • 11-JUC中的Condition对象

    文章目录 ConditionCondition常用方法总结参考 Condition 任何一个java对象都天然继承于Object类 xff0c 在线程间实现通信的往往会应用到Object的几个方法 xff0c 比如wait wait lon
  • 【Java面试题汇总】多线程、JUC、锁篇(2023版)

    导航 黑马Java笔记 踩坑汇总 Java基础 JavaWeb SSM SpringBoot 瑞吉外卖 SpringCloud 黑马旅游 谷粒商城 学成在线 设计模式 牛客面试题 目录 0 请你说说线程和进程的区别 1 请你说说多线程 2
  • 线程中断标志位 interrupt()、interrupted()、isInterrupted() 的认识

    常见问题 首先你是怎么去关闭一个开启的线程 调用中断方法之后 线程就立即停止运行吗 带着这两个问题探讨一下 主要围绕着这三个方法讲述 interrupt interrupted isInterrupted 归类为中断 什么是中断标识位 首先
  • 创建线程四种方法详解;及说明ThreadPoolExecutor方式创建线程池

    一 继承Thread类的方式 创建一个线程 class MyThred extends Thread public MyThred String name super name Override public void run 线程内的操作
  • JUC源码分析2-原子变量-AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray

    JUC针对数组元素的原子封装 先看AtomicIntegerArray private static final Unsafe unsafe Unsafe getUnsafe arrayBaseOffset获取数组首个元素地址偏移 priv
  • 【源码】走一遍源码弄清ArrayList容器的扩容机制

    源码 走一遍源码弄清ArrayList容器的扩容机制 首先我们来看看ArraysList容器在整个Java集合框架中所处的位置 由此可见ArrayList是Java集合框架中 两大派系中Collection接口的子接口List的实现类 我们
  • 并发容器(一):普通容器&&同步容器&&并发容器

    前言 之前我们学习过了集合 并发编程 现在我们来学习并发容器 在并发编程中 经常听到Java集合类 同步容器 并发容器 那么他们之间有哪些分类 优劣呢 我们先把这个框架给分清楚了 这样后面学习的时候不会乱 集合容器 大家熟知的集合类Arra
  • Java线程的同步机制(synchronized关键字)

    线程的同步机制 synchronized 1 背景 例子 创建个窗口卖票 总票数为100张 使用实现Runnable接口的方式 1 问题 卖票过程中 出现了重票 错票 gt 出现了线程的安全问题 2 问题出现的原因 当某个线程操作车票的过程
  • ThreadLocal从变量副本的角度解决多线程并发安全问题

    ThreadLocal从变量副本的角度解决多线程并发安全问题 之前我们讲的高并发场景下的线程安全问题 可以使用Synchronized同步关键字 Lock手动加锁的方式去解决 什么轻量级锁 偏向锁 重量级锁 可重入锁等等 实际上本质都是控制
  • AQS相关工实现类的使用及其原理

    文章目录 1 AQS 1 1 概述 1 2 自定义不可重入锁 2 ReentrantLock 2 1 非公平锁 2 1 1 加锁解锁流程 2 1 1 1 加锁失败 2 1 1 2 解锁竞争 2 2 可重入原理 2 3 可打断原理 2 3 1
  • Java线程池源码解析及使用

    1 线程池的用处 Java 引入 Excutor 框架将任务的提交和执行进行解耦 只需要定义好任务 然后提交给线程池即可 使用线程池的时机 单个任务处理时间比较短 需要处理的任务数量很大 线程池的优点 降低资源消耗 通过重复利用已创建的线程
  • JUC并发编程之AQS原理

    1 AQS 原理 1 1 概述 全称是 AbstractQueuedSynchronizer 是阻塞式锁和相关的同步器工具的框架 特点 用 state 属性来表示资源的状态 分独占模式和共享模式 子类需要定义如何维护这个生态 控制如何获取锁
  • JUC并发编程之Java线程(二)

    二 Java线程 2 1 创建和运行线程 方法一 Thread创建线程方式 继承Thread类 匿名内部类方式 public class CreateThread01 public static void main String args
  • 安全线程的集合

    1 CopyOnWriteArrayList package com kuang unsafe import java util import java util concurrent CopyOnWriteArrayList java u
  • java晋级赛 深入并发编程

    根据黑马java并发编程学习做的笔记 传送门 https www bilibili com video BV16J411h7Rd p 15 java晋级赛 深入并发编程 一 多线程基础 进程与线程 创建线程的方式及运行原理 创建线程的方式
  • 黑马并发编程(AQS源码分析、线程池)

    AQS源码分析 线程池 8 线程池 1 自定义线程池 阻塞队列 优化队列 线程池 执行和线程处理设计 线程池执行的整个思路 阻塞添加 拒绝策略 2 ThreadExecutor 线程池状态 线程池参数 拒绝策略 newFixedThread
  • JUC并发编程学习

    JUC并发编程学习 目录 JUC并发编程学习 1 什么是JUC 1 1 JUC简介 1 2 进程与线程 1 3 线程的状态 1 3 1 线程状态Thread State 枚举类 1 3 2 wait sleep 区别 1 4 并发与并行 1
  • 第5节 实现Callable 接口

    Java 5 0 在java util concurrent 提供了一个新的创建执行 线程的方式 Callable 接口 Callable 接口类似于Runnable 两者都是为那些其实例可能被另一个线程执行的类设计的 但是 Runnabl
  • JUC并发编程共享模型之管程(三)(中)

    4 5Monitor概念 Java 对象头 以 32 位虚拟机为例 在32位虚拟机中 1个机器码等于4字节 也就是32bit 在64位虚拟机中 1个机器码是8个字节 也就是64bit 普通对象 数组对象 其中Mark Word 结构为 最后

随机推荐

  • OriginPro,如何把软件Origin切换变成中文显示

    Origin其实自带中文界面 xff0c 很多朋友反馈不知道如何设置 xff0c 软件里面也没看到有改变语言的选项 xff0c 它设置需要更改注册表 xff0c 相对复杂一点 那么今天就教大家如何将origin的语言改为中文吧 下文以201
  • Java笔记(9)——static、静态初始化、单子模式、final关键字和抽象类

    1 static关键字 static xff1a 静态的 xff0c 意为全局的 xff0c 由该关键字标记的变量或者方法由整个类所共享 没有对象的实例 xff0c 可以使用类名 方法名 的形式访问由static修饰的类方法 在static
  • python做1到100的自然数累加

    python做1到100的自然数累加 xff0c 并将结果保存至同目录下的txt文件 s span class token operator 61 span span class token number 0 span i span cla
  • Centos7 yum安装VSCode

    Centos7 yum安装VSCode 安装密钥和存储库安装VSCode运行VSCode问题解决问题1问题2 桌面快捷方式参考 安装密钥和存储库 sudo rpm import https packages microsoft com ke
  • 6-8 求二叉树高度 (20 分)

    本题要求给定二叉树的高度 函数接口定义 xff1a 96 96 96 cpp int GetHeight BinTree BT 其中BinTree结构定义如下 xff1a typedef struct TNode Position type
  • copy outerHTML、python爬取csdn文章、一键打包个人csdn文章保存到本地

    文章目录 1copy outerHTML复制网站源码法1 1复制源码1 2 遇到的问题 2 python爬取CSDN博客文章 xff08 保存为html xff0c txt xff0c md xff09 2 1 安装依赖2 2 完整代码 3
  • android遇到的问题的总结----Activity和fragment之间相互跳转

    一 activity与activity之间跳转 xff1a span class token comment 第一个参数是上下文 span span class token comment 第二个参数是将要跳转的activity span
  • maven项目创建MyBatis教程

    先创建一个新的maven项目 然后把配置文件放pom xml中在刷新导入 span class token operator lt span dependencies span class token operator gt span sp
  • Vue3中axios的使用与封装

    文章目录 一 安装axios二 配置与封装三 引入与使用 一 安装axios span class token function npm span i axios s 二 配置与封装 src aixos request js http js
  • MySQL常见七种通用的Join查询练习题

    准备数据库表 t dept 和 t emp CREATE TABLE 96 t dept 96 96 id 96 int NOT NULL AUTO INCREMENT 96 deptName 96 varchar 30 DEFAULT N
  • MySQL索引分类

    主键索引 xff1a 设定为主键后数据库会自动建立索引 xff0c innodb为聚簇索引 单值索引 xff1a 即一个索引只包含单个列 xff0c 一个表可以有多个单列索引 唯一索引 xff1a 索引列的值必须唯一 xff0c 但允许有空
  • Docker 部署 MySQL 一主多从

    主从复制的原理 xff1a 1 主库 xff1a 创建一个有权访问binlog日志的从库账号 xff0c 配置需要主从复制的库 有写操作时 xff0c 可以将写操作或者写操作之后的数据记录到日志文件中 binlog 通过一个线程通知需要同步
  • Java笔记(8)——重载(Overload)与重写(Override)的区别

    1 重写 xff08 Override xff09 重写是子类对允许访问的父类的方法进行重新编写的过程 xff0c 方法名 返回值和参数列表不能变 xff0c 方法中的内容可以变化 特点就是 xff1a 子类可以根据自己的需要对父类的方法进
  • ShardingSphere介绍

    官网 xff1a https shardingsphere apache org index zh html 文档 xff1a https shardingsphere apache org document 5 1 1 cn overvi
  • ShardingSphere-JDBC读写分离

    基于之前搭建的mysql主从读写分离使用ShardingSphere JDBC实现读写分离 参考文章 xff1a Docker 部署 MySQL 一主多从 书启秋枫的博客 CSDN博客 CREATE DATABASE mydb2 USE m
  • ShardingSphere-JDBC垂直分片

    什么是数据分片 xff1f 简单来说 xff0c 就是指通过某种特定的条件 xff0c 将我们存放在同一个数据库中的数据分散存放到多个数据库 xff08 主机 xff09 上面 xff0c 以达到分散单台设备负载的效果 数据的切分 xff0
  • ShardingSphere-JDBC水平分片

    项目中可以使用ShardingSphere JDBC将数据存到不同库的表中 一 准备服务器 服务器规划 xff1a 使用docker方式创建如下容器 主服务器 xff1a 容器名server order0 xff0c 端口3310从服务器
  • ShardingSphere-JDBC绑定表

    一 什么是绑定表 指分片规则一致的一组分片表 使用绑定表进行多表关联查询时 xff0c 必须使用分片键进行关联 xff0c 否则会出现笛卡尔积关联或跨库关联 xff0c 从而影响查询效率 例如 xff1a t order 表和 t orde
  • ShardingSphere-JDBC广播表

    一 什么是广播表 指所有的分片数据源中都存在的表 xff0c 表结构及其数据在每个数据库中均完全一致 适用于数据量不大且需要与海量数据的表进行关联查询的场景 xff0c 例如 xff1a 字典表 广播具有以下特性 xff1a xff08 1
  • 01_JUC概述

    1 JUC是什么 xff1f 在 Java 5 0 提供了 java util concurrent 简称JUC 包 xff0c 在此包中增加了在并发编程中很常用的工具类 此包包括了几个小的 已标准化的可扩展框架 xff0c 并提供一些功能