java多线程_01(什么是多线程、线程安全)偏理论

目录

前言

多线程的知识点算是比较难啃的骨头,仅凭一篇文章很难把知识点系统的梳理完,所以多线程这个章节我分了理论篇和实操篇来解说梳理。该篇为理论篇,认识多线程的起点,后续我再抽时间继续跟进!

一、什么是java多线程

(一)、进程与线程

1、进程(一个运行的程序就是一个进程,进程之间资源不能共享)

 2、线程(一个进程中包含1~n个线程,同类线程间资源共享)

(二)、什么是多线程

1、串行

2、并行

 并行与并发的区别(附加):

多线程:

二、线程安全

(一)、什么是线程安全

线程安全

三、如何确保线程安全(加锁)

(一)、synchronized(译:同步;特点:自动上锁解锁)

synchronized(同步锁)关键字的两种使用方法:

1、synchronized加在方法上(有两种情况 - 需要注意关键字的作用域)

(2)、synchronized加在静态方法上(作用域:某个类的范围)

2、synchronized同步代码块

 这里在强调下线程安全锁:

(二)、Lock锁(特点:手动上锁、解锁)

Lock锁的实现步骤:

​​Lock与synchronized的区别:

Lock的另一种获取锁的方式(tryLock()方法):




前言

多线程的知识点算是比较难啃的骨头,仅凭一篇文章很难把知识点系统的梳理完,所以多线程这个章节我分了理论篇和实操篇来解说梳理。该篇为理论篇,认识多线程的起点,后续我再抽时间继续跟进!



一、什么是java多线程

在学习多线程之前,我们得先了解下在操作系统中进程线程的区别:

(一)、进程与线程

1、进程(一个运行的程序就是一个进程,进程之间资源不能共享)

电脑中会有很多单独运行的程序,每个程序都有一个独立的进程,而进程之间是相互独立存在的(说白了就是启动一个程序,就相当于开启了一个进程)。比如下图任务管理器中的eclipse、火狐浏览器、电脑管家、腾讯QQ等

 2、线程(一个进程中包含1~n个线程,同类线程间资源共享)

 每个进程都有独立的代码和数据空间,进程想要执行任务就需要依赖线程。换言之,就是进程中的最小执行单位就是线程,并且一个进程中包含1~n个线程。同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC:也就是cpu分配给各线程的时间片结束时最后执行的代码行数),线程间的切换开销小,而进程之间的切换开销较大。(进程是资源分配的最小单位,而线程是cpu调度的最小单位

(二)、什么是多线程

何为多线程?提到多线程就必须得理解串行和并行两个概念。

1、串行

串行指用单条线程来执行多个任务,就拿下载文件来举例:当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,也就是说,必须等下载完文件A之后才能开始下载文件B,它们在时间上是不可能发生重叠的。如图:

2、并行

并行指开启多个线程,下载多个文件,多个文件严格意义上进行同时下载,在同一时刻发生,并行在时间上是重叠的。

 并行与并发的区别(附加):

并发:指单核cpu运行多线程时,cpu分配时间片会进行很快的切换。线程轮流获取时间片执行任务。

并行:指多核cpu运行多线程时,真正的在同一时刻运行。

了解了串行和并行概念之后,我们再来说说什么是多线程。

多线程:

以我的联想电脑管家举例,电脑管家本身就是一个程序(也就是一个进程),它里面有很多功能,如下图:能优化加速、空间清理、病毒查杀等众多功能。

按串行来说:也就是单线程执行多任务。无论你是想要清理垃圾、还是要查杀病毒,都必须得先完成其中的一件事,才能做下一件事,这里面有一定的执行顺序。

如果是多线程的话,我们就可以清理垃圾的同时,还可以进行查杀病毒,电脑加速等等其它操作,这就是严格意义上的同一时刻发生的,没有执行上的先后顺序。这就是,一个进程运行时产生了多个线程。

认识多线程以后,我们又需要去了解一个使用多线程不得不考虑的问题——线程安全问题。


二、线程安全

(一)、什么是线程安全

既然是线程安全问题,那毫无疑问,所有的隐患都是在多个线程访问的情况下产生的。也就是我们要确保多条线程访问资源时,我们的程序能按我们预期的行为去执行,先看下面代码示例。

很简单的一段代码,下面通过运行结果来探究,多个线程同时访问会不会出现什么问题。我开启的3条线程,每个线程循环10次,得到一以下结果,如图:

通过运行结果可以看到,3个线程会重复打印同一个数字,出现这种情况表明该run()方法不是线程安全的,出现这种问题原因有很多。

最常见的一种,就是我们的线程1、2、3都进入run()方法后,拿到了num的值,线程1、2打印了num值,但是线程3的时间片到了,卡在了打印指令之前,当线程2再次进循环改变了num值且打印输出后,线程3才执行了打印指令。(说白了,线程不安全就是:多线程环境下共享同份资源,线程之间乱抢资源)

由此看来,这确实不是一个线程安全的类,因为这三个线程都需要操作这个共享的变量。SO我们根据这个程序来总结下什么是线程安全。

  • 线程安全

指当多个线程访问某个方法时,不管你通过怎样的调用方式,不管这些线程间如何交替执行,我们在主程序中都不需要去做任何同步,这个类的结果行为都是我们设想的正确行为,那我们就可以说这个类是线程安全的

三、如何确保线程安全(加锁)

既然存在线程安全问题,那就得有解决方案,我们看下常见的几种方式。

(一)、synchronized(译:同步;特点:自动上锁解锁)

synchronized关键字,用来控制线程同步的。保证我们的资源在多线程环境下,不被多个线程同时执行,确保我们数据的完整性。

synchronized(同步锁)关键字的两种使用方法:

1、synchronized加在方法上(有两种情况 - 需要注意关键字的作用域

(1)、synchronized加在(非静态的)普通方法上

(作用域:该类的某个对象实例内

如:synchronized aMethod(){},可以防止多个线程同时访问这个对象的synchronized同步方法(注意:此时的同步方法只对该类当前这个对象实例内起作用。此时同步锁,锁的是当前对象本身也就是当前的this,只能限制当前对象中线程之间不乱抢资源,而该类的其它对象根本就锁不住)。但当前线程访问的同时,其它线程照样可以同时访问相同类的其它对象实例中的synchronized方法。(遇到这种情况有两种解决方案:1、静态同步方法 2、类构造的单例模式)

(2)、synchronized加在静态方法上(作用域:某个类的范围

如:synchronized static aMethod{},防止多个线程同时访问这个类中的synchronized static方法。这种同步静态方法可以对类的所有实例对象起作用。(解析:因为静态的是属于类的,当类加载时在方法区中只初始化一次,所有类都只能共用这唯一的一份,所以不管该类有多少个对象实例,这个同步静态方法都能锁住,实现线程同步,真正做到防止多个线程同时访问类中的这个synchronized static方法)

2、synchronized同步代码块

synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。语法格式:synchronized(锁对象){区块}。

注意:锁对象必须是唯一的,多个线程共用同一把锁线程之间才能形成互斥,才能实现线程同步,防止多个线程同时访问同一个资源。

 这里在强调下线程安全锁:

  • 同步锁也叫对象锁,是用对象做为锁,不同的对象就是不同的锁。
  • synchronized关键字用于保证线程安全,是一种阻塞式的解决方案,让同一时刻最多只有一个线程能持有对象锁,其它线程再想获取这个对象锁就会被阻塞。注意:不要理解为一个线程加了锁,进入synchronized代码块中就会一直执行下去。如果时间片切换了,也会执行其它线程,再切换回来会紧接着执行(这也是依赖cpu的计数器)。只是不会执行到有竞争锁的资源,因为当前线程还未释放锁。
  • 当一个线程执行完synchronized的代码块后,会唤醒正在等待的线程。
  • synchronized关键字使用需注意:synchronized关键字是不能被继承的,也就是说父类的方法synchronized mehod(){}在子类中并不自动是synchronized mehod(){},而是变成method(){}。子类需要显性的指定它的某个方法为synchronized方法。

(二)、Lock锁(特点:手动上锁、解锁)

先说说Lock跟synchronized的区别,Lock是在java1.6被引入进来的,Lock的引入让锁有了可操控性,也就是我们可以在需要的时候去手动的获取锁和释放锁。注意Lock是一个接口,而synchronized是关键字。

Lock锁的实现步骤:

  • 创建Lock接口实现类ReentrantLock的对象
  • 调用ReentrantLock类的lock()上锁方法,上锁
  • 调用ReentrantLock类的unlock()解锁方法,解锁

Lock与synchronized的区别:

  • synchronized:java关键字层面上的锁,自动上锁、解锁
  • Lock:javaAPI层面上的锁,手动上锁、解锁

Lock的另一种获取锁的方式(tryLock()方法):

tryLock()方法跟lock()的区别,lock()在获取锁的时候,如果拿不到锁,就一直处于等待状态,直到拿到锁。而tryLock()会返回一个boolean值,如果没拿到锁,就直接返回false,停止等待,它不会像lock()那样一直等待获取锁。(也就是在线程访问资源时,发现锁被占用,在等待时间结束后就不再等待,可根据返回值做后续操作)注意tryLock()是可以设置等待时间参数的。利用这个特性可以模拟出访问超时返回给客户”网络繁忙,稍后重试“的实际案例。



该篇主要就三点:什么是多线程,线程安全,如何确保线程安全。后面会继续更新,敬请期待……

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值