线程、多线程和线程池面试题(四)

更新时间:2023-07-12 22:49:33 阅读: 评论:0

  不同点是,Sychronized是原⽣语法层⾯上同步控制,其颗粒度更⼤;相⽐⽽⾔,ReentranLock是从API层⾯来控制锁(lock()与unlock()⽅法),开发者的⾃主权更强,可控制粒度更细,优化空间更⾼
CAS指令与原⼦性
  与上述两种互斥同步不同,CAS是⼀种原⼦操作,因为互斥同步需要额外消耗时间,属于悲观策略,对于极⾼并发情况并不适合;⽽CAS则属于乐观锁,是通过内存值⽐较和替换来实现的。
  CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会⾃动将该位置值更新为新值 。否则,处理器不做任何操作。这种实现主要是cpu层的⽀持,因为CAS是⼀种系统原语,原语属于操作系统⽤语范畴,是由若⼲条指令组成的,⽤于完成某个功能的⼀个过程,并且原语的执⾏必须是连续的,在执⾏过程中不允许被中断,也就是说CAS是⼀条CPU的原⼦指令,不会造成所谓的数据不⼀致问题。所以利⽤CPU的CAS指令,同时借助JNI来完成Java的⾮阻塞算法。
volatile 关键字
 ⼀旦⼀个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
  1)保证了不同线程对这个变量进⾏操作时的可见性,即⼀个线程修改了某个变量的值,这新值对其他线程来说是⽴即可见的。
  2)禁⽌进⾏指令重排序。
摘⾃《深⼊理解Java虚拟机》:
  “观察加⼊volatile关键字和没有加⼊volatile关键字时所⽣成的汇编代码发现,加⼊volatile关键字时,会多出⼀个lock前缀指令”
  lock前缀指令实际上相当于⼀个内存屏障(也称内存栅栏),内存屏障会提供3个功能:
  1)它确保指令重排序时不会把其后⾯的指令排到内存屏障之前的位置,也不会把前⾯的指令排到内存屏障的后⾯;即在执⾏到内存屏障这句指令时,在它前⾯的操作已经全部完成;
  2)它会强制将对缓存的修改操作⽴即写⼊主存;
  3)如果是写操作,它会导致其他CPU中对应的缓存⾏⽆效。
3、为什么要有线程,⽽不是仅仅⽤进程?
微信头像卡通男生
  进程:系统进⾏资源分配和调度的⼀个最⼩单位
  线程:CPU进⾏调度和分派的最⼩单位,它和其他线程共享同⼀进程下的所有资源
可以说进程的颗粒度更⼤,⽽线程颗粒度更⼩,更适合并发执⾏多个任务以及需要共享资源的情况。
4、r un()和s tar t()⽅法区别
很明显,真正启动线程的⽅法就是start()⽅法,run()⽅法只不过是⼀个普通的⽅法,如果直接调⽤run()⽅法,还是在主线程顺序执⾏。
5、如何控制某个⽅法允许并发访问线程的个数?
使⽤Semaphore控制, Semaphore mSemaphore = new Semaphore(5);表⽰⽣成五个信号量的实例,保证只有5个线程可以同时执⾏;maphore.acquire()请求⼀个信号量,这个时候信号量个数 -1;
建军lea() 释放⼀个信号量,此时信号量个数+1;
6、在Jav a中wait和s e e lp⽅法的不同;
爱情就像一阵风
 wait 和 sleep 都能将线程状态变成等待状态,除了这点相同点,其它基本都不同。⽐如:
  1、wait()是定义在Object 类中的,是⼀个final修饰的⾮静态⽅法;⽽sleep是被定义在Thread类中的,是⼀个static修饰的静态⽅法;
  2、wait()必须⽤于同步环境,如synchronized⽅法或者同步代码块、⽣产-消费者队列,通常配合notify()⽅法使⽤,wait 让⽣产者线程等待,notify唤醒消费者线程,通知队列⾮空;⽽sleep()没有这个限制。
富足  3、调⽤wait()时会释放当前持有的锁,⽽sleep()不会释放任何锁。
7、谈谈wait/notif y关键字的理解
  如果线程调⽤了对象的wait()⽅法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
  当有线程调⽤了对象的notifyAll()⽅法(唤醒所有wait线程)或notify()⽅法(只随机唤醒⼀个wait线程),被唤醒的的线程便会进⼊该对象的锁池中,锁池中的线程会去竞争该对象锁。
关键字对⽐.png
在第⼆个问题中已经对这⼏种进⾏了说明;对于实际开发⽽⾔,Synchronized是⽐较基础简单的,涉及同步锁;volatile 关键字修饰共享变量,⼀个线程修改,其他线程⽴即可见;
⽽通过CAS,我们可以实现⼀种⾮阻塞同步策略,粒度更细,涉及CPU层⽀持,效率更⾼,是很多系统并发SDK的基础,如下⼏个包:Atomic:原⼦数据的构建。
Locks:基本的锁的实现,最重要的AQS框架和lockSupport
Concurrent:构建的⼀些⾼级的⼯具,如线程池,并发队列等。
12、如何保证线程安全?
见上
13、如何实现线程同步?
见上我阅读我快乐作文
14、两个进程同时要求写或者读,能不能实现?如何防⽌进程的同步?
  可以进⾏同时读写,但为了保证数据的正确,必须要针对进程访问的共享临界区进⾏处理;两个进程不能同时进⼊临界区,否则会导致数据错乱。常见的处理⽅式有:信号量、管程、会合、分布式系统
信号量
信号量是⼀个计数器,它只⽀持2种操作:P操作(进⼊临界区)和V操作(退出临界区)。假设有信号量SV,则对它的P、V操作含义如下:
  P(SV),如果SV的值⼤于0,意味着可以进⼊临界区,就将它减1;如果SV的值为0,意味着别的进程正在访问临界区,则挂起当前进程的执⾏;
  V(SV),当前进程退出临界区时,如果有其他进程因为等待SV⽽挂起,则唤醒之;如果没有,则将SV加1,之后再退出临界区。
管程
  提出原因:信号量机制不⾜,程序编写困难、易出错
  ⽅案:在程序设计语⾔中引⼊⼀种⾼级维护机制
  定义:是⼀个特殊的模块;有⼀个名字;由关于共享资源的数据结构及在其上操作上的⼀组过程组成。进程只能通过调⽤管程中的过程间接访问管程中的数据结构
  1)互斥:管程是互斥进⼊的 为了保证数据结构的数据完整性
管程的互斥由编译器负责保证的,是⼀种语⾔机制
  2)同步:设置条件变量及等待唤醒操作以解决同步问题
可以让⼀个进程或者线程在条件变量上等待(先释放管程的管理权),也可以通过发送信号将等待在条件变量上的进程线程唤醒
15、线程间操作L is t小鲳鱼
1、声明list的地⽅⽤Collections.synchronizedList()包⼀层,表⽰可以同步访问;
2、使⽤synchronized(this){}锁操作List
逻辑代码块
3、使⽤concurrent同步代码包中封装类
16、Jav a中对象的⽣命周期
1、创建阶段(Created)
开心激情播播网
  检测类是否被加载没有加载的先加载→为新⽣对象分配内存→将分配到的内存空间都初始化为零值→对对象进⾏必要的设置→执⾏<init>⽅法把对象进⾏初始化
对象的加载⼤⼩是类加载中就已经确定好了的,类加载过程就相当复杂了,如下图:武成帝
类加载过程.png
2、应⽤阶段(In U)
  ⾄少有⼀个强引⽤使⽤着
3、不可见阶段(Invisible)

本文发布于:2023-07-12 22:49:33,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1079022.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   操作   信号量
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图