网站首页 > 文章精选 正文
21.什么是线程,怎么创建一个线程
线程(thread)是操作系统能够进行运算调度的最小单位,一个线程就是一个单一顺序的控制流。
注意与进程的区别:进程是指在系统中正在运行的一个应用程序,一个程序下至少有一个进程,一个进程下也可以有多个线程,每个进程有自己各自的变量,而线程间共享数据。
创建线程方式:
·继承Thread类并重新run方法
·实现Runnable接口,执行方法是run()
·实现Callable接口,执行方法是call()
Runnable和Callable区别:
·是否有返回值:Callable能返回执行结果;而Runnable不能返回结果
·是否会抛出异常:Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛
22.线程的生命周期
新建:创建线程对象
就绪:有执行资格,没有执行权
运行:既有执行资格,又有执行权
阻塞:没有执行资格,没有执行权
死亡:线程对象变成垃圾,等待回收
注意sleep和wait的区别:
·来自的类不同:sleep来自Thread类,而wait来自Object类
·是否释放锁:sleep不释放锁,而wait方法释放了锁。sleep不出让系统资源,使用wait当前线程会进入线程等待池等待,出让系统资源,其他线程可以占用CPU。
·使用范围不同:sleep方法可以在任何地方使用,wait方法只能在同步方法和同步代码块中使用
23.保证多线程运行安全的方法
·尽量使用一些线程安全类和Atomic开头的原子类
·使用final修饰,不可变的对象一定是线程安全的
·使用ThreadLocal让变量在每个线程中都保存一份副本,避免数据在多线程中共享
·使用volatile修饰关键的需要共享的变量
·使用CAS比较比较并交换
·加锁:
·自动锁synchronize关键字
·手动锁ReentrantLock类
public static void main(String[] args) {
Lock myLock = new ReentrantLock();
myLock.lock();
...
myLock.unlock();
}
注意:CAS的缺点
·CAS失败时,会一直进行尝试。如果CAS长时间一直不成功,会给CPU带来很大的开销。
·只能保证一个共享变量的原子操作
·ABA问题:如果内存中的V和A相同,就更新V为新的预期值B,但这并不代表内存中V一直没变过。可能V的值曾经被其他线程改变为B,又变回了A,再被比较,CAS就会误认为它从来没有被改变过。Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性。
24.synchronized实现原理
synchronized也要分为两个版本分析,在Java6之前,synchronized是重量级锁,对于系统开销太大,Java6对synchronized进行了大幅优化,提供了三种锁机制,大大提高了其性能。
Java 虚拟机中的synchronized是基于进入(monitorenter)和退出管程(monitorexit)实现。但在方法上添加synchronized是特例,JVM是调用在常量池中ACC_SYNCHRONIZED 标志来隐式实现的。
我们先需要了解一下synchronized锁对象的存在:
对象存在堆内存中,主要包括三个部分:对象头,实例变量和填充数据
对象头主要又由两个部分组成:Markword和Classpoint,每个部分储存内容见下表:
Markword中就记录着锁的类型是无锁、偏向锁、轻量级锁还是重量级锁,在Java6之前只有重量级锁。
对于Java6之前的synchronized来说,只有重量级锁,主要通过monitorenter和monitorexit来实现,每个对象在创建时都会同时创建自己的monitor监视器,当一个 monitor 被某个线程持有后,它便处于锁定状态,每个monitor各自维护了一个计数器count,调用monitorenter就是尝试获取这个对象,如果count!=0,就阻塞,等待锁释放;如果count=0,则该线程获取成功,count数加1,如果重复进入,则继续加1,线程退出就执行monitorexit,count数减1。
因为monitor是依赖于底层的操作系统来实现的,而操作系统实现线程之间的切换时需要从用户态转换到核心态,时间开销太大,所以Java6之后,对synchronized进行了大幅的优化,下面讲一下synchronized锁升级过程。
25.锁升级过程
锁升级的过程包括无锁、偏向锁、轻量级锁还有重量级锁,其中锁只能升级或者释放为无锁,不会出现锁的降级。
·无锁:没有对资源进行锁定,所有线程都可以访问修改,但只有一个锁能成功
·偏向锁:当锁竞争不强时,通常一个线程会多次获取同一个锁,此时偏向锁可以减少获取锁的代价。
当线程1访问并获取锁时,会在对象头和值栈记录线程1的ThreadID,由于偏向锁不会主动释放锁,又有线程来访问锁时,会比较这个线程的ID和自己对象头里的ThreadID是否一致,如果一致,则不需要加锁解锁,如果不一致,就会先去查看线程1是否存活,如果存活再查找值栈信息,如果线程1还需要持有这个对象,就升级为轻量级锁,不需要就变成无锁
·轻量级锁:线程1升级为轻量级锁后,就会把锁对象的Markword拷贝一份到自己的值栈中,然后CAS 操作把对象头中的内容替换为自己值栈的指针,如果存在竞争激烈,CAS操作失败,就会自旋尝试获取锁
·重量级锁:自旋一定次数后依旧获取不到锁时,就升级为重量级锁
猜你喜欢
- 2024-12-26 Java高级:条件队列与同步器Synchronizer的原理+AQS的应用
- 2024-12-26 浅谈Java多线程与并发原理 java多线程并发调用接口
- 2024-12-26 Java 基础(四)集合源码解析 List java集合linkedlist
- 2024-12-26 synchronized和lock的区别 54.synchronized 和 lock 有什么区别?
- 2024-12-26 异步 vs 同步:程序员必备的核心知识,理解这两者差异,你就是高手
- 2024-12-26 ArrayList 、 LinkedList、Vector的区别
- 2024-12-26 java面试基础题(实战后的总结) java面试必考300题
- 2024-12-26 synchronized底层细究(硬核) synchronized底层原理是什么
- 2024-12-26 为什么 95% 的 Java 程序员,都是用不好 Synchronized?
- 2024-12-26 100+道高频Java面试题 java面试高频知识点
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)