线程同步和线程死锁

记一次线程池使用导致的死锁 top统计的是当前统计窗口里的情况。一般情况下,只要不是宿主机上的所有容器负载都很大,超卖是没有影响的,即只要没有把物理机cpu打满,超卖就超卖,反正也用不完,但是只要某一时刻兄弟们要求的12个cpu都要用的时候,那就完了,物理机cpu使用率飙高,这个时候进程上下文切换更加频繁,雪上加霜,系统会更慢的。但还有一点人性没有泯灭,当时这么写的时候,其实有点膈应,但是一个闪念的想法让我就这么写了:任务里的操作其实会很快,因为有两层缓存,即走到了db,也一定走上索引,应该不会慢的,就这样吧,应该问题不大。 阅读详情
1.线程同步,一个关键字:synchronized

为什么有这个东西呢,假如有一个对象,里面有成员变量和方法,如果有很多线程都想访问它们,有可能造成用户想避免的结果。

我也举那个经典的例子:假如你的银行账户里面有2000块钱,有一天你去银行柜台取钱,取1500,正在你办理的时候,你老婆去了取款机,她也取钱,事先没商量好谁取,所以她也想取1500。如果两个人都取走了1500,合起来就3000了,银行咋办???

我们把这个银行账户当作一个类来看待,里面有一个钱的成员变量,有一个对钱的数量进行加减的方法,一个getMoney的方法。

在柜台取钱和在取款机取钱分别为2个线程。当两个线程同时访问银行账户这个类的对象的时候。都调用了钱的减法运算的方法,并通过getMoney方法拿到了1500块钱,所有人都这样干,于是,银行破产了。

解决方法,同步。

我在方法声明的时候前面加一个synchronized关键字,public synchronized void method(){ },它代表的意思是在执行这个方法的时候当前对象被锁定起来。

Java中的每个对象都有一个lock,当访问某个对象的synchronized方法时,该对象就会被上锁(注意,是对象,不是方法,假如你在这个类中定义了多个方法,如果你的线程访问到了其中的任意一个synchronized方法,那么其它的就暂时不能被访问了,必须等到该对象被解锁以后,即方法执行结束才行)。解锁的意思是值线程执行该方法完毕,或者说过程中抛出了异常。

再换一种说法,就是,一个类中有synchronized方法,如果该类的对象的该方法被访问时,那么整个该对象都被锁定了,但是这个意思是其它非synchronized方法和成员变量还是可以被访问,注意区分这一点。因为synchronized方法会锁定对象,所以一旦有一个synchronized方法被某个线程启动了,那么对象已经被独占了,其它的synchronized方法就不能再同时独占对象了,但是普通方法和成员变量并不独占对象,所以仍然可以被调用。

需要注意的是,如果同步方法里面有sleep方法,它仍然是同步方法的一部分,在它被执行的过程中,锁仍然不会被解开。

其实同步的意思就是上锁,同步方法,进而达到对象上锁的目的。假如有一个数据库,有读和修改2个方法,你可以允许多个线程同时读,但是你不能让多个线程同时改,所以说改的方法要同步,读的方法不需要。(其实这里我更加觉得应该同步的不是方法,而是数据本身,只要有对象访问对象,对象就应该被锁定,避免读的时候有对象要修改,修改的时候有对象要读,甚至是多个对象同时都想改)。还有,如果2个方法都修改了同一个值的话,那么2个方法都应该加同步。

线程同步我觉得是这样的,你说概念吧,也还不难理解,我觉得真正难的是实际中的应用,你必须考虑很多相关的问题,哪一个方法要同步,都需要好好琢磨。

在这里说3个方法,wait,notify,notifyAll。

之前说过一个方法叫做sleep,通常来说你按照自己的经验和感觉要求线程睡眠一定的时间。但是,有时候当你不知道需要线程睡眠多久的时候,sleep方法就不行了,必须使用wait。但是记住,wait只能用于同步方法。用法大概可以这样,比如你可以先进行一个while判断(不推荐用if,假如有excpetion发生的话,就不再判断直接执行后面的,这样可能还是有问题,所以最好用while,即使exception发生了,仍然会进行判断),如果满足一定条件就this.wait,然后不满足了就this.notify。如果有很多同步方法的话,那么也可以使用notifyAll方法,那么在这个对象上面等着的线程都会被叫醒。

synchronized关键字囊括了所有和同步有关的东西。除此之外,还有一个关键字volatile,它只能用来同步基本类型的成员变量。数据的写入通常来说是通过缓存写入内存的,使用volatile的原理就是它会绕过缓存,直接写入内存。读取数据的时候同样也直接从内存读取,这样就可以有效地避免数据不同步的情况。

同步的几个准则:
a.首先,尽量使得synchronized块保持简短。你锁的东西越多,越可能造成死锁。
b.不要在synchronized块中调用那些可能引起阻塞的方法,比如read。
c.如果持有了锁的话,不要对其它对象调用方法。

2.线程死锁
既然可以上锁,那么假如有2个线程,一个线程想先锁对象1,再锁对象2,恰好另外有一个线程先锁对象2,再锁对象1。
在这个过程中,当线程1把对象1锁好以后,就想去锁对象2,但是不巧,线程2已经把对象2锁上了,也正在尝试去锁对象1。
什么时候结束呢,只有线程1把2个对象都锁上并把方法执行完,并且线程2把2个对象也都锁上并且把方法执行完毕,那么就结束了,但是,谁都不肯放掉已经锁上的对象,所以就没有结果,这种情况就叫做线程死锁。

其中一个解决方法就是加大锁定的粒度,也就是尽量锁大的对象,不要锁得太小,还有尽量不要同时锁2个或2个以上的对,但是还有待于进一步研究。

3.wait和notify和notifyAll
主要是用来让线程之间互相通知事件的发生。

1).wait
Object类中的final方法,有InterruptedException。它的作用是导致当前的线程等待,直到其它线程调用此对象的notify方法或者notifyAll方法,wait还有一些重用方法,传参数,比如说时间长度。
当前的线程必须拥有此对象监视器,然后该线程发布对此监视器的所有权并且开始等待,直到其它线程通过调用notify方法或者notifyAll方法,通知在此对象的监视器上等待的线程醒来,然后该线程将等到重新获得对监视器的所有权后才能开始执行。
说说wait和sleep的区别
首先sleep
sleep是Thread里面的方法,在被执行的时候,锁并不会被交出去,要直到sleep所在的方法全部被执行完毕以后才交出锁。
wait是Object里面的方法,在被执行的时候,锁被解除,由其它线程去争夺,直到有notify或者notifyAll方法唤醒它。

2).Notify
也是Object类中的方法,用于唤醒在此对象上等待着的某一个线程,如果有很多线程挂起的话,就随机地决定哪一个。注意,是随机的,这时可以用notifyAll来唤醒所有的。一定要注意这个问题,除非你明确地知道你在做什么,否则最好就是用notifyAll。

注意事项:
wait()和notify()必须包括在synchronized代码块中,等待中的线程必须由notify()方法显式地唤醒,否则它会永远地等待下去。很多人初级接触多线程时,会习惯把wait()和notify()放在run()方法里,一定要谨记,这两个方法属于某个对象,应在对象所在的类方法中定义它,然后run中去调用它。

【Cesium开发实战】解决加载白膜数据位置不合适问题(地底或浮空) 解决白膜位置地底或者浮空问题。 阅读详情

相关推荐

【3GPP核心网】【5G】史上最全的5G协议规范总结

物理层信道定义:TS38.211(物理层信号的生成、调制,DMRS额外DMRS,PRACH configuration index,Point A等介绍)传输信道控制信道的数据处理:TS38.212(包括复用,信道编码,交织,DCI,DMRS端口号等)物理层控制过程:TS38.213(同步,上行功控,随机接入,UE上报接收控制消息的过程,SSB等)物理层测量:TS38.215(UE网络的测量控制,UE测量能力)频率、频点相关F R1:TS38.101-1。

风云说通信 3115

线程池的死锁事件

线程池的死锁问题

scwMason的博客 1394

Excel预算专项模板-100平米房屋装修全包预算

Excel预算专项模板-100平米房屋装修全包预算

线程池+死锁

常见的线程池: (1)Executors.newCacheThreadPool():可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中。(可无限扩充,负载较轻的情况使用) (2) Executors.newFixedThreadPool(int n):创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。(用于负载较重的情况使用,控制线程数量) (3)Executors.newScheduledThreadPool(int n):创建一个定长线

njh1147394013的博客 1493

【Linux线程(四)】死锁&&线程

Linux死锁线程池相关知识

G17861308017的博客 976

多线程死锁详解

一、什么是死锁 当两个或两个以上的线程在执行过程中,因为争夺资源而造成的一种相互等待的状态,由于存在一种环路的锁依赖关系而永远地等待下去,如果没有外部干涉,他们将永远等待下去,此时的这个状态称之为死锁。 经典的 “哲学家进餐” 问题很好地描述了死锁状况: 5个哲学家去吃中餐,坐在一张圆桌旁,他们有5根筷子(而不是5双),并且每两个人中间放一根筷子,哲学家们要么在思考,要么 在进餐,每个人都需...

牧小农 1万+

线程同步死锁

同步,多个线程访问同一资源 引出问题 范例:以卖票为例。 class MyThread extends Thread{//表示实现多线程 private int ticket = 5; public void run() {//覆写run()方法线程的主方法 for(int x=0;x<10;x++){ if(this.ticket>0){ System.out...

SILENCE_LM_的博客 320

【Linux】线程同步死锁

本文章主要介绍了线程同步的相关知识死锁的相关知识。其中线程同步包括实现同步的一种方式:条件变量,然后讲解了条件变量,以及条件变量的使用等等。

weixin_47257473的博客 689

线程同步死锁

前天俺们谈到了加锁(线程同步),但是在使用加锁的同时又会带来一个问题,就是死锁。 什么叫死锁? 所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 发生死锁的原因一般是两个对象的锁相互等待造成的。 那么为什么会产生死锁呢? 1.因为系统资源不足。 2.进程运行推进的顺序不合适。     3.资源分配不当

LPPTREE的专栏 530

Java多线程的实现同步死锁

package threadTest; /** * @author : fanpc * @date : 2020-12-11 10:59 **/ import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; public class FileCopy implements Runnable { public void run() { for (int i

橙子橙子大橙子的博客 1344

线程同步解决死锁

当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。这个时候,有个单线程模型下不存在的问题就来了:如果多个线程同时读写共享变量,会出现数据不一致的问题。上面的代码很简单,两个线程同时对一个int变量进行操作,一个加10000次,一个减10000次,最后结果应该是0,但是,每次运行,结果实际上都是不一样的。这是因为对变量进行读取写入时,结果要正确,必须保证是原子操作。原子操作是指不能被中断的一个或一系列操作。

m0_60301012的博客 851

Linux:线程同步概述死锁介绍(什么是线程同步、为什么要进行线程同步、有哪些线程同步的手段、死锁概念、死锁的条件、死锁的后果、死锁的场景场景)

Linux:线程同步概述死锁介绍(什么是线程同步、为什么要进行线程同步、有哪些线程同步的手段、死锁概念、死锁的条件、死锁的后果、死锁的场景场景)

qq_42279379的博客 436

线程死锁

文章目录前言一、什么是死锁?二、死锁是怎样产生的?三、怎样实现一个死锁?四、死锁的排查预防?总结 前言 什么是死锁死锁是怎样产生的?怎么实现一个死锁?如何解决死锁?可能我们日常工作中很少用到这些相关内容,但是随着要求越来越高,这些东西不了解是不行的,下面对死锁做一个简单的记录。 一、什么是死锁? 先看一下百度百科对死锁的定义: 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这.

雅俗共赏的博客 960

多线程——死锁详解

死锁是指两个或两个以上的进程在执⾏过程中,由于竞争资源或者由于彼此通信⽽造成的⼀种阻塞的现象,若⽆外⼒作⽤,它们都将⽆法推进下去。(也就是两个线程拥有锁的情况下,⼜在尝试获取对⽅锁,从⽽造成程序⼀直阻塞的情况。

qq_55072036的博客 1974

线程安全问题

当多个线程共享一个资源时,则可能出现线程安全问题。java解决线程安全的方式有三种第一种: 同步代码块第二种: 同步方法第三种: Lock锁。

m0_63955162的博客 634

死锁原因及解决方案

在应用程序开发的过程中,有时候线上会遇到死锁问题,死锁一般有操作系统级别的死锁应用程序级别的死锁,操作系统级别的死锁通常发生的是进程死锁,应用程序级别的死锁通常是线程死锁,本文主要谈谈线程死锁问题。 一、java线程死锁 1、死锁的原因 (1) 因竞争资源发生死锁现象:系统中供多个线程共享的资源的数目不足以满足全部进程的需要时,就会引起对诸资源的竞争而发生死锁现象 (2)推进顺序不当发生死锁 2、线程死锁的条件 ...

fengchengwu2012的博客 4834

线程池终极指南 :如何避免线程池中的死锁问题

Java的世界中,线程池是提升应用性能资源利用率的关键技术。本文将深入探讨线程池的运行原理,应用场景,并结合源码分析,为你提供一份全面的线程池设置指南。通过理解线程池的运行原理,我们可以根据不同的应用场景任务类型,设置合适的线程池大小。这只是线程池配置的一小部分,实际使用中,我们还需要根据应用的需要进行更多的配置。线程死锁问题的解决方法通常涉及对线程池的使用进行调整,以及对应用程序的并发设计进行优化。如果线程池的任务队列已满,可以使用自定义的拒绝策略来处理新提交的任务,而不是让线程等待获取锁。

赵KK的博客 1495
上一篇: 线程的2种实现方式
下一篇: 网络编程
superhill
博客等级 码龄17年 106粉丝 135原创
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值