步伐人生——Java多线程和并发的使用建议,都是精髓

打印 上一主题 下一主题

主题 942|帖子 942|积分 2826

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数步伐员,想要提升技能,每每是本身摸索成长,但本身不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新软件测试全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。





既有得当小白学习的零基础资料,也有得当3年以上履历的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!
由于文件比较多,这里只是将部分目次截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,而且后续会持续更新
如果你必要这些资料,可以添加V获取:vip1024b (备注软件测试)

正文





    • 建议119:启动线程前stop方法是不可靠的



  • 建议120:不实用stop方法停止线程


  • 建议121:线程优先级只使用三个等级


    • 建议122:使用线程非常处置惩罚器提升系统可靠性



  • 建议123:volatile不能包管数据同步


  • 建议124:异步运算思量使用Callable接口


    • 建议125:优先选择线程池



  • 建议126:适时选择差别的线程池来实现


  • 建议127:Lock与synchronized是不一样的


    • 建议128:防备线程死锁



  • 建议129:得当设置阻塞队列长度


  • 建议130:使用CountDownLatch和谐子线程


    • 建议131:CyclicBarrier让多线程齐步走



  • 深入认识JVM


    • JVM内存分配,类加载



  • 创建对象的4种方法总结
  • 垃圾接纳GC
  • JVM调优,Arthas使用


  • 认识多线程


    • 创建多线程方法+相识线程池



  • 多线程下-1非原子性问题即解决
  • 再论线程,创建、生命周期


  • 总结
引出

步伐人生——Java多线程和并发的使用建议

多线程和并发

建议118:不保举覆写start方法



  • 继承自Thread类的多线程类不必覆写start方法。原本的start方法中,调用了当地方法start0,它实现了启动线程、申请栈内存、运行run方法、修改线程状态等职责,线程管理和栈内存管理都是由JVM实现的,如果覆盖了start方法,也就是撤销了线程管理和栈内存管理的本领。所以除非必要,否则不要覆写start方法,即使必要覆写start方法,也必要在方法体内加上super.start调用父类中的start方法来启动默认的线程操作
建议119:启动线程前stop方法是不可靠的



  • 征象:使用stop方法停止一个线程,而stop方法在此处的目标不是停止一个线程,而是设置线程为不可启用状态。但是运行结果出现奇怪征象:部分线程照旧启动了,也就是在某些线程(没有规律)中的start方法正常执行了。在不符合判定规则的情况下,不可启用状态的线程照旧启用了,这是线程启动(start方法)一个缺陷。Thread类的stop方法会根据线程状态来判定是闭幕线程照旧设置线程为不可运行状态,对于未启动的线程(线程状态为NEW)来说,会设置其标记位为不可启动,而其他的状态则是直接停止。start方法源码中,start0方法在stop0方法之前,也就是说即使stopBeforeStart为true(不可启动),也会先启动一个线程,然后再stop0结束这个线程,而罪魁祸首就在这里!所以不要使用stop方法进行状态的设置
建议120:不实用stop方法停止线程



  • 线程启动完毕后,必要停止,Java只提供了一个stop方法,但是不建议使用,有以下三个问题:1、stop方法是过时的;2、stop方法会导致代码逻辑不完备,stop方法是一种“恶意”的停止,一旦执行stop方法,即终止当前正在运行的线程,不管线程逻辑是否完备,这是非常伤害的,以为stop方法会扫除栈内信息,结束该线程,但是大概该线程的一段逻辑非常重,比如子线程的主逻辑、资源接纳、情景初始化等,由于stop线程了,这些都不会再执行。子线程执行到那边会被关闭很难定位,这为以后的维护带来了很多麻烦;3、stop方法会破坏原子逻辑,多线程为相识决共享资源抢占的问题,使用了锁概念,制止资源差别步,但是stop方法会丢弃全部的锁,导致原子逻辑受损。Thread提供的interrupt停止线程方法,它不能终止一个正在执行着的线程,它只是修改停止标记唯一。总之,渴望终止一个正在运行的线程,不能使用stop方法,必要自行编码实现。如果使用线程池(比如ThreadPoolExecutor类),那么可以通过shutdown方法徐徐关闭池中的线程
建议121:线程优先级只使用三个等级



  • 线程优先级保举使用MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY三个级别,不建议使用其他7个数字)(线程的优先级(Priority)决定了线程获得CPU运行的机会,优先级越高,运行机会越大。事实:1、并不是严酷尊重线程优先级别来执行的,分为10个级别;2、优先级差别越大,运行机会差别越大;对于Java来说,JVM调用操作系统的接口设置优先级,比如Windows是通过调用SetThreadPriority函数来设置的。差别操作系统线程优先级设置是不相同的,Windows有7个优先级,Linux有140个优先级,Freebsd有255个优先级。Java缔造者也发现了该问题,于是在Thread类中设置了三个优先级,建议使用优先级常量,而不是1到10随机的数字
建议122:使用线程非常处置惩罚器提升系统可靠性



  • 可以使用线程非常处置惩罚器来处置惩罚相关非常情况的发生,比如当机自动重启,大大提高系统的可靠性。在实际环境中应用注意以下三点:1、共享资源锁定;2、脏数据引起系统逻辑混乱;3、内存溢出,线程非常了,但由该线程创建的对象并不会马上接纳,如果再重新启动新线程,再创建一批新对象,特殊是加入了场景接管,就伤害了,有大概发生OutOfMemory内存泄露问题
建议123:volatile不能包管数据同步



  • volatile不能包管数据是同步的,只能包管线程能够获得最新值)(volatile关键字比较少用的原因:1、Java1.5之前该关键字在差别的操作系统上有差别的体现,移植性差;2、比较难设计,而且误用较多。在变量钱加上一个volatile关键字,可以确保每个线程对当地变量的访问和修改都是直接与主内存交互的,而不是与当地线程的工作内存交互的,包管每个线程都能获得最“新鲜”的变量值。但是volatile关键字并不能包管线程安全,它只能包管当前线程必要该变量的值时能够获得最新的值,而不能包管多个线程修改的安全性
建议124:异步运算思量使用Callable接口



  • 多线程应用的两种实现方式:一种是实现Runnable接口,另一种是继承Thread类,这两个方式都有缺点:run方法没有返回值,不能抛出非常(归根到底是Runnable接口的缺陷,Thread也是实现了Runnable接口),如果必要知道一个线程的运行结果就必要用户自行设计,线程类本身也不能提供返回值和非常。Java1.5开始引入了新的接口Callable,类似于Runnable接口,实现它就可以实现多线程任务,实现Callable接口的类,只是表明它是一个可调用的任务,并不表示它具有多线程运算本领,照旧必要执行器来执行的
建议125:优先选择线程池



  • Java1.5以前,实现多线程比较麻烦,必要本身启动线程,并关注同步资源,防止出现线程死锁等问题,Java1.5以后引入了并行盘算框架,大大简化了多线程开辟。线程有五个状态:新建状态(New)、可运行状态(Runnable,也叫作运行状态)、阻塞状态(Blocked)、等待状态(Waiting)、结束状态(Terminated),线程的状态只能由新建转变为运行态后才大概被阻塞或等待,最后闭幕,不大概产生舍本逐末的情况,比如想把结束状态变为新建状态,则会出现非常。线程运行时间分为三个部分:T1为线程启动时间;T2为线程体运行时间;T3为线程烧毁时间。每次创建线程都会颠末这三个时间会大大增加系统的响应时间。T2是无法制止的,只能通过优化代码来低落运行时间。T1和T3都可以通过线程池(Thread Pool)来缩短时间。线程池的实现涉及一下三个名词:1、工作线程(Worker),线程池中的线程只有两个状态:可运行状态和等待状态;2、任务接口(Task),每个任务必须实现的接口,以供工作线程调治器调治,它主要规定了任务的入口、任务执行完的场景处置惩罚、任务的执行状态等。这里的两种类型的任务:具有返回值(或非常)的Callable接口任务和无返回值并兼容旧版本的Runnable接口任务;3、任务队列(Work Queue),也叫作工作队列,用于存放等待处置惩罚的任务,一般是BlockingQueue的实现类,用来实现任务的排队处置惩罚。线程池的创建过程:创建一个阻塞队列以容纳任务,在第一次执行任务时闯将足够多的线程(不超过许可线程数),并处置惩罚任务,之后每个工作线程自行从任务队列中获得任务,直到任务队列中任务数量为0为止,此时,线程将处于等待状态,一旦有任务加入到队列中,即唤醒工作线程进行处置惩罚,实现线程的可复用性
建议126:适时选择差别的线程池来实现



  • Java的线程池实现从根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,照旧父子关系。为了简化并行盘算,Java还提供了一个Executors的静态类,它可以直接生成多种差别的线程池执行器,比如单线程执行器、带缓冲功能的执行器等,归根结底照旧以上两个类的封装类
建议127:Lock与synchronized是不一样的



  • Lock类(显式锁)synchronized关键字(内部锁)用在代码块的并发性和内存上时的语义是一样的,都是保持代码块同时只有一个线程具有执行权。显式锁的锁定和开释必须在一个try…finally块中,这是为了确保即使出现运行期非常也能正常开释锁,包管其他线程能够顺遂执行。Lock锁为什么不出现互斥情况,全部线程都是同时执行的?原因:这是由于对于同步资源来说,显式锁是对象级别的锁,而内部锁是类级别的锁,也就是说Lock锁是跟随对象的synchronized锁是跟随类的,更简单地说把Lock定义为多线程类的私有属性是起不到资源互斥作用的,除非是把Lock定义为全部线程共享变量。除了以上差别点之外,还有以下4点差别:1、Lock支持更细粒度的锁控制,假设读写锁分离,写操作时不允许有读写操作存在,而读操作时读写可以并发执行,这一点内部锁很难实现;2、Lock是无阻塞锁,synchronized是阻塞锁,线程A持有锁,线程B也渴望获得锁时,如果为Lock,则B线程为等待状态,如果为synchronized,则为阻塞状态;3、Lock可实现公平锁,synchronized只能是非公平锁什么叫做非公平锁?当一个线程A持有锁,而线程B、C处于阻塞(或等待)状态时,若线程A开释锁。JVM将从线程B、C中随机选择一个线程持有锁并使其获得执行权,这叫做非公平锁(由于它扬弃了先来后到的次序);若JVM选择了等待时间最长的一个线程持有锁,则为公平锁。必要注意的是,即使是公平锁,JVM也无法正确做到“公平”,在步伐中不能以此作为正确盘算。显式锁默认是非公平锁,但可以在构造函数中加入参数true来声明出公平锁;4、Lock是代码级的,synchronized是JVM级的,Lock是通过编码实现的,synchronized是在运行期由JVM表明的,相对来说synchronized的优化大概性更高,毕竟是在最核心不为支持的,Lock的优化必要用户自行思量。相对来说,显式锁使用起来更加便利和强盛,在实际开辟中选择哪种类型的锁就必要根据实际情况思量了:机动、强盛则选择Lock,快捷、安全则选择synchronized
建议128:防备线程死锁



  • 线程死锁(DeadLock)是多线程编码中最头疼问题,也是最难重现的问题,由于Java是单进程多线程语言。要达到线程死锁必要四个条件:1、互斥条件;2、资源独占条件;3、不剥夺条件;4、循环等待条件;按照以下两种方式来解决:1、制止或减少资源贡献;2、使用自旋锁,如果在获取自旋锁时锁已经有保持者,那么获取锁操作将“自旋”在那里,直到该自旋锁的保持者开释了锁为止
建议129:得当设置阻塞队列长度



  • 阻塞队列BlockingQueue扩展了Queue、Collection接口,对元素的插入和提取使用了“阻塞”处置惩罚。但是BlockingQueue不能够自行扩容,如果队列已满则会报IllegalStateException:Queue full队列已满非常;这是阻塞队列非阻塞队列一个重要区别:阻塞队列的容量是固定的,非阻塞队列则是变长的。阻塞队列可以在声明时指定队列的容量,若指定的容量,则元素的数量不可超过该容量,若不指定,队列的容量为Integer的最大值。有此区别的原因是:阻塞队列是为了容纳(或排序)多线程任务而存在的,其服务的对象是多线程应用,而非阻塞队列容纳的则是平凡的数据元素。阻塞队列的这种机制对异步盘算是非常有帮助的,如果阻塞队列已满,再加入任务则会拒绝加入,而且返回非常,由系统自行处置惩罚,制止了异步盘算的不可知性。可以使用put方法,它会等队列空出元素,再让本身加入进去,无论等待多长时间都要把该元素插入到队列中,但是此种等待是一个循环,会不绝地消耗系统资源,当等待加入的元素数量较多时势必会对系统性能产生影响。offer方法可以优化一下put方法
建议130:使用CountDownLatch和谐子线程



  • CountDownLatch和谐子线程步调:一个开始计数器,多个结束计数器:1、每一个子线程开始运行,执行代码到begin.await后线程阻塞,等待begin的计数变为0;2、主线程调用begin的countDown方法,使begin的计数器为0;3、每个线程继续运行;4、主线程继续运行下一条语句,end的计数器不为0,主线程等待;5、每个线程运行结束时把end的计数器减1,标记着本线程运行完毕;6、多个线程全部结束,end计数器为0;7、主线程继续执行,打印出结果。类似:领导安排了一个大任务给我,我一个人不大概完成,于是我把该任务分解给10个人做,在10个人全部完成后,我把这10个结果组合起来返回给领导—这就是CountDownLatch的作用
建议131:CyclicBarrier让多线程齐步走



  • CyclicBarrier关卡可以让全部线程全部处于等待状态(阻塞),然后在满意条件的情况下继续执行,这就好比是一条起跑线,不管是如何到达起跑线的,只要到达这条起跑线就必须等待其他人员,待人员到齐后再各奔东西,CyclicBarrier关注的是汇合点的信息,而不在乎之前或者之后做那边理。CyclicBarrier可以用在系统的性能测试中,测试并发性
深入认识JVM

JVM内存分配,类加载

Java进阶(1)——JVM的内存分配 & 反射Class类的类对象 & 创建对象的几种方式 & 类加载(何时进入内存JVM)& 注解 & 反射+注解的案例

创建对象的4种方法总结

Java进阶(4)——结合类加载JVM的过程理解创建对象的几种方式:new,反射Class,克隆clone(拷贝),序列化反序列化

网上学习资料一大堆,但如果学到的知识不成体系,碰到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
必要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注软件测试)

一个人可以走的很快,但一群人才气走的更远!岂论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、口试辅导),让我们一起学习成长!
添加V获取:vip1024b (备注软件测试)**
[外链图片转存中…(img-4lyob7S4-1713235740325)]
一个人可以走的很快,但一群人才气走的更远!岂论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、口试辅导),让我们一起学习成长!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小小小幸运

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表