Java 线程基础

news2024/10/2 20:38:16

volatile

在这里插入图片描述

happens-before原则

1、单一线程原则 在一个线程内,在程序前面的操作线性发生于后面的操作

2、管程锁定原则

一个unlock操作先行发生于后面对同一个锁的lock操作

3、volatile变量原则

对一个volatile变量的写操作先行发生于后面对变量的读操作

4、线程启动规则

Thread对象的start()方法调用先行发生于此线程的每一个动作

5、线程加入规则

Thread对象的结束先行发生于join()方法返回

6、线程中断规则

对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过interrupted()方法检测到是否有中断行为发生

7、对象终结规则

一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始

8、传递性

如果操作A先行于操作B发生,操作B先行发生于操作C,则操作A先行发生于操作C。

线程安全

1、不可变

不可变的对象一定是线程安全的
不可变的类型:

  • final关键字修饰的基本数据类型
  • String
  • 枚举类
  • Number的部分子类,如Long和Double等包装类型,但AtomicInteger和AtomicLong是可变的

对于集合类型,使用Collections.unmodifiableXXX()可以获取一个不可变的集合

Map<String,Integer> map = new HashMap<>();
Map<String,Integer> unmodifyableMap = Collections.unmodifiableMap(map);
unmodifyableMap.put("key",0);

上述代码抛出异常:UnsupportedOperationException

2、绝对线程安全

不管运行时环境如何,调用者都不需要任何额外的同步措施

3、相对线程安全

在一些特定的场合需要额外的同步手段来保证线程安全

4、线程兼容

线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确使用同步手段来保证对象在并发环境中可以安全得使用。
平常说线程不安全,大部分指该种情况

5、线程对立

不管怎么样都没法多线程运行的代码

线程安全的实现方法

1、互斥同步

synchronized 和 ReentrantLock

2、非阻塞同步

(1)CAS

比较并交换Compare-and-Swap
CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。

(2)AtomicInteger

AtomicInteger,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作

(3)ABA

如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。

J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。

3、无同步方案

(1)栈封闭

多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的。

(2)线程本地存储

如果一段代码中所需要的数据必须与其它代码共享,那就看这些共享数据的代码是否能保证在同一个线程中执行。
可以使用 java.lang.ThreadLocal 类来实现线程本地存储功能。
在这里插入图片描述

public class ThreadLocalExample1 {
    public static void main(String[] args) {
        ThreadLocal threadLocal1 = new ThreadLocal();
        ThreadLocal threadLocal2 = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
            threadLocal1.set(1);
            threadLocal2.set(1);
        });
        Thread thread2 = new Thread(() -> {
            threadLocal1.set(2);
            threadLocal2.set(2);
        });
        thread1.start();
        thread2.start();
    }
}

(3)可重入代码(Reentrant Code)

这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身)

线程基础

线程状态转换

在这里插入图片描述

新建(new)

创建后未启动

可运行(runnable)

可能正在运行 也可能正在等待CPU时间片

阻塞(blocking)

等待获取一个排他锁,如果其它线程释放了锁就会结束此状态

无限期等待(waiting)

等待其它线程显式的唤醒,否则不会被分配CPU时间片
在这里插入图片描述

限期等待(timed waiting)

无需等待其它线程唤醒,在一定时间后自动唤醒
使用Thread.sleep()进入 称为使一个线程睡眠
使用Object.wait()进入 称为挂起一个线程
在这里插入图片描述

死亡(terminated)

线程结束

线程使用方式

  • 实现Runnable接口
  • 实现Callable接口
  • 继承Thread类

实现Runnable接口

需要实现run()方法 调用start()来启动线程

public class MyRunnable implements Runnable{
    private String message;

    public void run() {
        System.out.println(message);
    }

    public MyRunnable(String message) {
        this.message = message;
    }
}

实现Callable接口

与Runnable相比,Callable可以有返回值,返回值通过FutureTask进行封装

public class MyCallable implements Callable<Integer> {
    public Integer call() {
        return 123;
    }
}

继承Thread类

同样需要实现run()方法

public class MyThread extends Thread {
    public void run() {
        // ...
    }
}

使用匿名内部类和lambda表达式

public class CreatingThread03 {
    public static void main(String[] args) {
        // Thread匿名类,重写Thread的run()方法
        new Thread() {
            @Override
            public void run() {
                System.out.println(getName() + " is running");
            }
        }.start();

        // Runnable匿名类,实现其run()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " is running");
            }
        }).start();
    }
}

lambda表达式更简单,因为只需要实现一个run方法,lambda表达式可以进一步简写
注:lambda写法实质上是匿名实现了runable接口

new Thread(()->{
            System.out.println(Thread.currentThread().getName() + " is running");
        }).start();

基础线程机制

Executor

Executor管理多个异步任务的执行

  1. CacheedThreadPool:一个任务创建一个线程
  2. FixedThreadPool:所有任务只能使用固定大小的线程
  3. SingleThreadExecutor:相当于大小为1的FixedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0;i < 5;i++) {
    executorService.execute(new MyRunnable("你好" + i));
}
executorService.shutdown();

Daemon

守护线程是程序运行时在后台提供服务的线程
使用setDaemon()将一个线程设置为守护线程

sleep()

Thread.sleep(millisec)方法会休眠当前正在执行的线程,单位ms

线程中断

一个线程执行完成之后会自动结束,如果在运行中结束会抛出异常

InterruptedException

通过调用interrupt()来中断线程,如果改线程处于阻塞、限期等待或者无限期等待状态,那么就会抛出InterruptedException

interrupted()

如果一个线程的 run() 方法执行一个无限循环,并且没有执行 sleep() 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt() 方法就无法使线程提前结束。

但是调用 interrupt() 方法会设置线程的中断标记,此时调用 isinterrupted() 方法会返回 true。因此可以在循环体中使用 isinterrupted() 方法来判断线程是否处于中断状态,从而提前结束线程。

Executor的中断操作

调用Executor的shutdown()方法会等待线程都执行完毕后再关闭,但是如果调用的是shutdownNow()方法,则相当于调用每个线程的interrup()方法

线程互斥同步

java有两种锁机制来控制多个线程对共享资源的互斥访问,第一个是JVM实现的synchronized 另一个是JDK实现的ReentrantLock

synchronized

1、同步一个代码块

public void func() {
    synchronized (this) {
        // ...} 
}

它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步

SynchronizedExample e1 = new SynchronizedExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> e1.func1());
executorService.execute(() -> e1.func1());

上面这种情况,如果func1()拥有同步,则两个线程不会同时进行

SynchronizedExample e1 = new SynchronizedExample();
SynchronizedExample e2 = new SynchronizedExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> e1.func1());
executorService.execute(() -> e2.func1());

这样则会同时进行
2、同步一个方法

public synchronized void func () {
    // ...
}

和同步代码块一样,作用于同一个对象
3、同步一个类

public void func () {
    synchronized (SynchronizedExample.class) {
        // ... }
}

作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步
4、同步一个静态方法

public synchronized static void fun() {
    // ...
}

作用于整个类

ReentrantLock

ReentrantLock是java.util.concurrent中包含的锁

public class LockExample {
    private Lock lock = new ReentrantLock();

    public void func () {
        lock.lock();
        try {
            for (int i =0 ; i < 10 ; i ++) {
                System.out.println(i + " ");
            }
        } finally {
            lock.unlock();
        }
    }
}
LockExample lockExample = new LockExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> lockExample.func());
executorService.execute(() -> lockExample.func());

该锁也是对同一个对象而言的,对于不同对象,仍然是同时进行的

线程之间的协作

join()

如果在一个线程中 调用另外一个线程的join()方法 会将当前线程挂起,直到目标线程结束

wait() notify() notifyAll()

调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。

它们都属于 Object 的一部分,而不属于 Thread。

只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateExeception

await() signal() signalAll()

java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/836173.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

资深测试经验总结,APP测试-关键点整理,少走3年的弯路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 APP测试它的关键点…

LeetCode--HOT100题(16)

目录 题目描述&#xff1a;238. 除自身以外数组的乘积&#xff08;中等&#xff09;题目接口解题思路代码 PS: 题目描述&#xff1a;238. 除自身以外数组的乘积&#xff08;中等&#xff09; 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等…

el-table那些事

el-table那些事 获取el-table所有勾选的行数据 用于记录工作和日常学习遇到的坑&#xff0c;需求。 vue3element-plusts 获取el-table所有勾选的行数据 1、需要先声明一个ref变量&#xff0c;并赋值给el-table 2、通过el-table提供的getSelectionRows()函数获取选中的"行…

嵌入式系统:连接数字世界的未来

嵌入式设备是专用微型计算机&#xff0c;由硬件、系统级软件和应用软件三个层次组成。硬件包括处理器、存储和各种I/O接口。系统级软件涵盖操作系统和各种I/O子系统。应用软件包括基于不同操作系统的开发。 几乎所有带数字接口的设备都使用嵌入式系统&#xff0c;从大型飞机到…

媒介易讲解体育冠军助力品牌解锁市场营销新玩法

在当今竞争激烈的市场中&#xff0c;品牌推广成为企业取得商业成功的重要一环。然而&#xff0c;随着传统市场推广方式的日益饱和&#xff0c;企业急需创新的市场营销策略来吸引消费者的关注和认可。在这样的背景下&#xff0c;体育冠军助力品牌成为了一种备受瞩目的市场营销新…

MongoDB文档-进阶使用-MongoDB索引-createindex()与dropindex()-在MongoDB中使用正则表达式来查找

阿丹&#xff1a; 之前研究了MongoDB的基础增删改查。在学会基础的数据库增删改查肯定是不够的。这个时候就涉及到了数据库搜索的时候的效率。需要提高数据的搜索效率。 MongoDB索引 在所以数据库中如果没有数据索引的时候。如果需要查找到一些数据。都会去主动扫描所有可能存…

无涯教程-Perl - 面向对象

Perl中的面向对象概念很大程度上基于引用以及匿名数组和哈希。让我们开始学习面向对象Perl的基本概念。 定义类 在Perl中定义一个类非常简单。类以最简单的形式对应于Perl软件包。要在Perl中创建一个类&#xff0c;我们首先构建一个包。 Perl软件包在Perl程序中提供了一个单…

Java-day05(面向对象-1)

面向对象 面向对象与面向过程的区别&#xff1a; 面向过程&#xff0c;强调功能行为&#xff1b;面向对象&#xff0c;强调功能的对象。 Java类及类成员 类&#xff1a;对一类事物描述&#xff0c;是抽象的&#xff0c;概念上的定义对象&#xff1a;实际存在的该类事物的每…

(自控原理)线性系统的频域分析法

目录 一、频率特性 1、频率特性的基本概念 2、频率特性的几何表示方法 二、典型环节与开环系统的频率特性 1、典型环节 2、开环对数的频率特性曲线 三、稳定裕度 1、相角裕度 2、幅值裕度 一、频率特性 1、频率特性的基本概念 2、频率特性的几何表示方法 二、典型环节…

<van-empty description=““ /> 滚动条bug

使用 <van-empty description"" /> 时&#xff0c;图片出现了个滚动条&#xff0c;图片可以上下滑动。 代码如下&#xff1a; <block wx:if"{{courseList.length < 0}}"><van-empty description"" /> </block> <…

跨境电商怎么做?Live Market教你创业及做大生意

随着全球化的不断深入和互联网技术的迅猛发展&#xff0c;跨境电商成为了一个蓬勃发展的行业。根据eMarketer的数据&#xff0c;2021年全球跨境电商销售额将达到4.5万亿美元&#xff0c;预计到2025年将增长至6.3万亿美元。这表明&#xff0c;跨境电商行业将继续保持强劲增长的趋…

uni-app:分页实现多选功能

效果 代码解析 一、标签-列表 <view class"item_all" v-for"(item, index) in info" :key"index"><view class"position parameter-info text-over" :class"{checked_parameter: item.checked}" :data-id"i…

EditPlus取消自动.bak备份

Tools->Preferences->File 将√取消

Java正则校验密码至少包含:字母数字特殊符号中的2种

一、语法 字符说明\将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如&#xff0c; n匹配字符 n。\n 匹配换行符。序列 \\\\ 匹配 \\ &#xff0c;\\( 匹配 (。^匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性&#xff0c;^ 还会与"\n…

如何开发一个企业ERP系统

企业ERP系统是一种针对企业管理和运营的综合性管理软件&#xff0c;旨在提高企业的效率和生产力。随着信息化时代的发展&#xff0c;越来越多的企业意识到引入ERP系统的重要性。本文将详细介绍如何开发一个企业ERP系统&#xff0c;为读者提供丰富、详细的信息。 一、需求分析 …

基于SpringBoot+Vue的地方美食分享网站设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

SQL SERVER ROW_NUMBER、RANK、DENSE_RANK 分页应用

** ROW_NUMBER 、RANK、DENSE_RANK** ROW_NUMBER () over( order by 列) # 不论【列】是否存在重复&#xff0c; 序号都不会重复的&#xff0c;通常用唯一个标识的列 RANK() over(order by 列) # 列 存在重复&#xff0c; 则 顺序号一致&#xff0c; 两个人并列第一&#xff0c…

SpringBoot Plus+代码生产器

0目录 1. Mybatis Plus 2.代码生产器 1.Mybatis Plus 创建数据库和表&#xff08;id没有设置主键和自增长&#xff09; 创建springBoot导入依赖 安装lombok 配置yml 实体类加入注解 无参构造和有参构造 Mapper接口 扫描接口 测试 加入日志 添加 数据库…

Springboot 多数据源 dynamic-datasource动态添加移除数据源

0.前言 上一篇文章我们讲了如何通过多数据源组件&#xff0c;在Spring boot Druid 连接池项目中配置多数据源&#xff0c;并且通过DS注解的方式切换数据源&#xff0c;《Spring Boot 配置多数据源【最简单的方式】》。但是在多租户的业务场景中&#xff0c;我们通常需要手动的…

Python:Spider爬虫工程化入门到进阶(2)使用Spider Admin Pro管理scrapy爬虫项目

Python&#xff1a;Spider爬虫工程化入门到进阶系列: Python&#xff1a;Spider爬虫工程化入门到进阶&#xff08;1&#xff09;创建Scrapy爬虫项目Python&#xff1a;Spider爬虫工程化入门到进阶&#xff08;2&#xff09;使用Spider Admin Pro管理scrapy爬虫项目 目录 1、使…