【学习笔记】多线程

news2024/12/27 15:31:05

1、线程基础

1.1 创建线程的几种方式

  • 继承Thread 类,覆盖run方法
  • 实现Runable接口。实现run方法。然后 通过Thread类构造方法获取Thread对象。
  • 实现Callable接口。实现call方法。 call方法可以抛出异常。也可以有返回值。
    run与start 调用run方法任然是主线程在执行。调用start才会启动多线程
public class A {
    static class MyCall implements Callable<String> {
        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName());
            return "call";
        }
    }

    public static void main(String[] args) throws Exception {
        FutureTask<String> futureTask = new FutureTask<>(new MyCall());
        Thread thread = new Thread(futureTask, "T1");
        thread.start();
        //主线程会阻塞,直到futureTask线程执行完获取到返回结果才会继续执行
        String s = futureTask.get();
        System.out.println(s);
    }
}

1.2 Thread的几个常用方法

  • Thread.sleep(ms) 当前线程执行等待操作。xxx毫秒后继续执行。
  • Thread.yield() 当前线程放弃当前时间片。(有可能下个时间片还是给到当前线程)
  • t1.join() 当前线程等待。执行t1线程。直到t1线程执行完成再执行当前线程。
  • wait notify ????

1.3 线程之间的状态转化

  • 新建 刚new出来的线程出于这个状态
  • ready 调用start方法,但是还未获取时间片
  • running 调用start方法,且未获取时间片
  • TimeWaiting 嫌弃等待 调用 sleep方法
  • Waiting 无限期等待 调用join方法 wait方法
  • block 阻塞 。等待锁标记
  • Teminated 终止状态。该状态不可逆
    在这里插入图片描述

1.4 synchronized 同步锁

  • 对方法或者代码块进行加锁。加锁的方法或者代码块同一个时间段只允许一个线程访问
  • 加在普通方法上 等价于对this进行加锁
  • 加在静态方法上等价于对类对象进行加锁
  • synchronized并不能解决乱序问题
  • 偏向锁
    偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
  • 轻量级
    轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
  • 重量级锁
    重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
  • 异常锁:synchronized 锁代码块发生异常时会释放锁标记。
public class A {
    /**
     * 可重入锁验证
     */
    public static void main(String[] args) throws Exception {
        A a = new A();
        Thread t1 = new Thread(() -> a.m1(), "t1");
        Thread t2 = new Thread(() -> a.m1(), "t2");
        t1.start();//m1,m2都是同一把锁。所以t1都可以访问
        t2.start();//t2 一直在等待
    }

    synchronized void m1() {
        while (true) {
            System.out.println("m1----" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
            }
            m2();
        }
    }

    synchronized void m2() {
        System.out.println("m2----" + Thread.currentThread().getName());
    }
}

1.5 volatile

  • 保证线程可见性

堆空间中的成员变量是线程共享的。当线程要修改这个变量时需要先把值copy到自己的工作内存中(操作数栈)。待修改完成后再写回去。在这期间若变量的值被别的线程改变。就会影响数据的一致性。
添加volatile关键字时。jvm在编译时会通过 ACC_VOLATILE标记
jvm层面通过读写屏障 cpu层面通过MESI协议保证线程的可见性。
volatile并不能保证原子性

volatile内存区的读写 都加屏障
StoreStoreBarrier
volatile 写操作
StoreLoadBarrier
LoadLoadBarrier
volatile 读操作
LoadStoreBarrier

  • 单例模式的双重检查
public class AAA {
    private static AAA a;

    public static AAA getInstance() {
        if (a == null) {
            synchronized (AAA.class) {
                if (a == null) {
                    a = new AAA();
                }
            }
        }
        return a;
    }
}

以上代码虽然加了锁但是任然会存在问题。有可能AAA在刚完成初始化的时候就把引用指向了A。这个时候其他线程就可能得到未初始化的对象。因此要给属性a 加volatile 关键字。禁止指令重排序。

CAS锁,乐观锁

  • jdk实现 AtomicXXXX 比如 AtomicInteger 通过compareandSet执行修改操作。先比较再赋值。
  • cas实现原理。

cas的计算会有三个值 v Expected newValue
V就是操作变量的当前值 Expect是期望值即执行运算之前的值。 newValue就是执行运算之后的值。当前两个值相同时。写入新的值否则就要更新Expected的值然后进行重试。

  • ABA问题
  • 如果是基本类型无影响。
  • 如果是引用类型。可以通过添加版本号来实现。每次修改version加一。AtomicStampedReference实现。
  • AQS:

ReentrantLock

同synchronized一样都是可重入锁。,他的底层是CAS锁。

  • 与synchronized区别:
    • synchronized发生异常以后会自动释放锁。reentrantLock不会。需要手动释放。
    • ReentrantLock可以使用tryLockf方法。无论是否取得锁不影响后续操作。
    • 可打断。加锁时选择lockInterruptibly方法加锁。可以使用interrupt方法打断其他线程强制占有锁
    • 公平锁 (ReenttrantLock 默认是不公平锁,所有等待的线程一起抢锁。没有先来后到) ReentrantLock lock=new ReentrantLock(true) 开启公平锁。谁先来谁先拿到锁。

CountDownLatch

相当于一个同步器。在创建CountDownLatch的时候指定线程数量。每个线程完成的时候调用一下countDown方法。然后可以使用await()方法阻塞线程。等所有线程结束后才继续执行。

    public static void main(String[] args) throws Exception{
        CountDownLatch countDownLatch = new CountDownLatch(100);
        Thread [] threads=new Thread[100];
        for (int i = 0; i < threads.length; i++) {
            threads[i]=new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"  is running");
                countDownLatch.countDown();
            },"thread-"+i);
        }
        for (int i = 0; i < threads.length; i++) {
            threads[i].start();
        }
        countDownLatch.await();//线程会在该地方阻塞。直到所有线程执行完毕才会打印over
        System.out.println("over");
    }

CyclicBarrier

线程栅栏。定义时会指定围栏大小。如果线程执行过程中调用了await方法。该线程就会等待直到等待线程达到栅栏指定的阈值

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierTest {
    public static void main(String[] args) {
        //指定等待线程的大小。如果没有那么多的线程。那么将永远阻塞
        CyclicBarrier barrier = new CyclicBarrier(100, () -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("发车了");//该代码由到达阈值的线程执行。
        });
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"is running");
                    TimeUnit.SECONDS.sleep(2);
                    barrier.await();//等运行的线程数到达阈值才会继续执行
                    System.out.println(Thread.currentThread().getName()+"is running");//等队列满了才会执行此代码
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },"thread-"+i).start();
        }
    }
}

LockSuport

# AQS队列(AbstractQueuedSynchronizer)
>- AQS队列又被称为CLH队列它由一个状态值 `volatile int state`和一个`双向链表`组成。state由多个线程抢占。谁拿到这个status就表示谁抢占到这把锁。AQS的核心就是一个共享数据,一堆线程去相互抢夺占用。这个就是AQS
>- 在reentrantLock中当`getStatus()`为1时表示锁已经被占用。0表示没有被占用。
>- 双向表中的node节点存储的是要抢占节点的线程。
>- 当发现status为1时会加入队列中(`addWaiter()`)等待锁释放以后再去抢占锁。
>- 当获取到status为0时,用cas锁尝试将status值设置为1.如果成功掉用setExclusiveOwnerThread.吧当前线程设置为独占这把锁的状态。

# ThreadLocal
>多个线程访问ThreadLocal中的变量。只能获取到他自己set的数据。
## 1、ThreadLocal源码解析
```java
public void set(T value) {
	//获取当前线程
	Thread t = Thread.currentThread();
	//通过当前线程获取对应map 这里的map对象对应 Thread.threadLocals 
	//它是一个Map<ThreadLocal,<T>>其中 T是要存储的数据。
	//一个线程可以绑定多个ThreadLocal对象。
	ThreadLocalMap map = getMap(t);
	if (map != null)
		//key为threadLocal对象,value为存储的值
		//如果之前set过会被覆盖
		map.set(this, value);
	else
		//没有则创建一个新的map
		createMap(t, value);
}

//很简单一目了然
ThreadLocalMap getMap(Thread t) {
	return t.threadLocals;
}
//没有就新创建一个
void createMap(Thread t, T firstValue) {
	t.threadLocals = new ThreadLocalMap(this, firstValue);
}

public T get() {
	//获取当前线程
	Thread t = Thread.currentThread();
	//获取 t.threadLocals 
	ThreadLocalMap map = getMap(t);
	//如果之前设置过 t.threadLocals应该不为空
	if (map != null) {
		//t.threadLocals的格式 Map<ThreadLocal,<T>>
		ThreadLocalMap.Entry e = map.getEntry(this);
		if (e != null) {
			@SuppressWarnings("unchecked")
			T result = (T)e.value;
			return result;
		}
	}
	//返回默认值具体默认值由子类的initialValue方法实现。可自定义
	return setInitialValue();
}

java的四种引用

  • 一般引用(强引用) A a=new A(); a是一个强引用。它指向的对象不会被回收。
  • 软引用 SoftRefence<T> m=new SoftRefence<T>() 内存不够时会被回收。非常适合做页面缓存
  • 弱引用 WeakRefence<T> m= new WeakRefence<T>();只要遇到gc就会被回收
  • 虚引用 jvm内部使用

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

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

相关文章

这个 Python 游戏库,打开就能玩一天

会 Python 的小伙伴&#xff0c;选择用 Python 让“大风车”吱呦呦地转&#xff01;还有的小伙伴&#xff0c;选择用 Turtle 库绘制童年的卡通人物 我们其实还可以选择用 Python 开发小游戏&#xff0c;回忆童年的美好时光。 “凹凸版”吃豆子 这次并非用pygame制作的&#…

MyBatis超详细学习笔记(黑马)

目录 一、MyBatis快速入门 &#xff08;一&#xff09;打开MyBatis中文官网 &#xff08;二&#xff09;在工程中&#xff08;pom.xml&#xff09;导入MyBatis依赖 &#xff08;三&#xff09;编写MyBatis核心配置文件——替换连接信息&#xff0c;解决硬编码问题 &#x…

关于加强网络舆情监测的几点建议,TOOM强化舆情监控有方法

网络舆情监测是一项旨在通过监测网络上的舆情信息&#xff0c;了解社会舆论和网络话语状况&#xff0c;分析舆情动态&#xff0c;预测舆情走向&#xff0c;并进行舆情管控的工作。主要通过计算机技术和信息科学手段对网络信息进行收集、整理、分析和报告&#xff0c;以便于管理…

Golang - 操作Redis

Golang - 操作Redis go-redis是Golang语言连接、操作Redis服务的客户端&#xff0c;几乎包含了对Redis的所有操作&#xff0c;具体如下&#xff1a; 简单易用 兼容多种Redis部署架构,可用于Redis服务器、Redis群集、Redis Sentinel&#xff0c;甚至Redis服务器环go-redis 支持…

配置TF-A源码

配置TF-A源码 1.对tf-a源码进行解压 $> tar xfz tf-a-stm32mp-2.2.r2-r0.tar.gz 2.打补丁 进入/home/ubuntu/FSMP1A/tf-a-stm32mp-2.2.r2-r0/tf-a-stm32mp-2.2.r2目录 执行 for p in ls -1 ../*.patch; do patch -p1 < $p; done 3.配置工具链 1)进入/home/ubuntu/FS…

05 CSS-CSS语法【尚硅谷JavaWeb教程】

05 CSS-CSS语法【尚硅谷JavaWeb教程】 JAVAWEB的学习笔记 学习视频来自&#xff1a;https://www.bilibili.com/video/BV1AS4y177xJ/?vd_source75dce036dc8244310435eaf03de4e330 为什么需要CSS 传统的园区网络采用设备和链路冗余来保证高可靠性&#xff0c;但其链路利用率低、…

Task7:动态函数

目录注意一 Filter函数二 Subtotal函数注意 filter&#xff0c;目前只有office365支持 一 Filter函数 作用&#xff1a;需要根据指定的条件&#xff0c;将符合条件的所有记录从数据源表格式查找过来之前方法&#xff1a; 用高级筛选&#xff08;缺点&#xff1a;在查询下一个…

SAP ADM100-Unit4 数据库工作原理:中央数据库管理与DBA Cockpit

概览 本节介绍DBA Cockpit,介绍SAP环境下数据库管理和监控的要点。 使用DBA Cockpit计划日历去计划数据和日志信息的周期性备份。 课程目标 备份数据库内容 检查数据库备份是否成功被执行。 1、DBA Cockpit总览 为了最少的系统停机时间和更高的系统性能,必须计划定期的…

云计算|OpenStack|社区版OpenStack安装部署文档(一 --- 前期硬件准备和部署规划)

前言&#xff1a; 社区版OpenStack是比较难以安装部署的&#xff0c;本文将就安装部署做一个详细的说明。 首先&#xff0c;OpenStack社区版本众多&#xff0c;如何选择一个合适的版本是第一个要解决的问题&#xff08;这里的合适是指的OpenStack版本和操作系统的版本合适&am…

MATLAB-自动控制原理-时域分析

目录 step函数&#xff08;求阶跃响应&#xff09;: impulse函数&#xff08;求脉冲响应&#xff09;: lsim函数&#xff08;求输出&#xff09;: 1&#xff0c;求二阶系统不同阻尼比条件下的阶跃响应曲线 2&#xff0c;求二阶负反馈系统的动态性能指标 利用MATLAB可以方…

五、好友关注,feed流推送

文章目录关注和取消关注&#xff0c;共同关注Feed流推送Feed介绍Timeline推模式Feed流滚动分页滚动分页查询收邮箱官方命令文档&#xff1a;https://redis.io/commands/ 关注和取消关注&#xff0c;共同关注 需求&#xff1a; 可以对一个用户进行关注和取消关注查询和一个用…

深刻理解状态机设计需要避免的冒险;处理单元里的control和datapath;时序电路可能存在essential hazard;竞争冒险【SV】【VLSI】

深刻理解状态机设计需要避免的冒险&#xff1b;处理单元里的control和datapath&#xff1b;时序电路可能存在essential hazard&#xff1b;竞争冒险【SV】【VLSI】0. 前言&#xff1a;时序电路可能存在essential hazard1. 理解control和datapath1.1 Datapath control2. 硬件电路…

JavaScript XHR、Fetch

前后端分离的优势 ◼ 早期的网页都是通过后端渲染来完成的&#xff1a;服务器端渲染&#xff08;SSR&#xff0c;server side render&#xff09;&#xff1a; \qquad 客户端发出请求-> 服务端接收请求并返回相应HTML文档-> 页面刷新&#xff0c;客户端加载新的HTML文…

如何采用conda配置python虚拟环境

文章目录一、创建python虚拟环境二、配置刚创建的虚拟环境三、将虚拟环境配置到相应项目一、创建python虚拟环境 首先选中要配置环境的文件 如下&#xff1a; 在此处输入cmd按回车 此处我创建一个环境名为hands3dtext&#xff0c;环境版本为3.7.2的初始环境 conda create -n…

Visual Studio 2015配置OpenCV4.5.3(c++版)

学习目标学会在Visual Studio 2015部署Opencv一个简单的C Opencv实例一、 Visual Studio 2015配置Opencv4.5.31.1 Visual Studio 2015网上关于Visual Studio 2015的下载&#xff0c;也有很多介绍。大家自行搜索安装。1.2 OpenCVOpenCV大家根据需求下载相应版本&#xff0c;官网…

ObjectARX如何修改多行文字的格式

一 AutoCAD中多行文字的格式设置 AutoCAD在设置多行文字格式时使用了非常特殊的方式&#xff0c;多行文字整体可以有诸多格式属性&#xff0c;比如字高、颜色、旋转角度等等&#xff1b;但是&#xff0c;多行文字的每一个字符其实也可以有自己单独的格式。 比如&#xff0c;下…

java ssm学校二手服饰交易系统服装商城的卖家

目 录 Abstract 1 第一章 绪论 2 1.1课题背景 2 1.2课题研究的目的和意义 3 1.3论文所做的主要工作 3 第二章 技术介绍 4 2.1 B/S架构 4 2.2 MySQL 介绍 4 2.3 JSP技术介绍 4 第三章 系统分析与设计 6 3.1 可行性分析 6 3.2系统说明 6 3.…

Hexo 添加 Github 贡献图

参考&#xff1a;https://akilar.top/posts/1f9c68c9/ 安装 hexo-filter-gitcalendar 插件 执行命令安装 hexo-filter-gitcalendar 插件 npm install hexo-filter-gitcalendar --save在站点配置文件 _config.yml 或者主题配置文件如 _config.butterfly.yml 中添加 # hexo-filte…

jmeter录制脚本及报错:Could not create script recorder解决办法

本文分两部分&#xff1a;一、录制脚本最简单步骤&#xff1b;二、报错解决步骤 一、录制脚本步骤 1、右击【测试计划】添加一个线程组&#xff08;用于存放录制的脚本&#xff09;&#xff1b; 2、右击测试计划添加一个HTTP代理服务器&#xff08;添加--非测试元件--HTTP代理…

(mysql的相关操作)和(Navicat与MySQL的连接使用)

mysql的相关操作 详细教程 链接1: http://edu.jb51.net/mysql/mysql-tutorial.html 链接2: http://c.biancheng.net/view/2441.html mysql安装教程&#xff1a;https://blog.csdn.net/weixin_36369605/article/details/113599400?ops_request_misc%257B%2522request%255Fid%…