精华内容
下载资源
问答
  • 一、死锁的定义多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都...

    一、死锁的定义

    多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

    下面我们通过一些实例来说明死锁现象。

    先看生活中的一个实例,2个人一起吃饭但是只有一双筷子,2人轮流吃(同时拥有2只筷子才能吃)。某一个时候,一个拿了左筷子,一人拿了右筷子,2个人都同时占用一个资源,等待另一个资源,这个时候甲在等待乙吃完并释放它占有的筷子,同理,乙也在等待甲吃完并释放它占有的筷子,这样就陷入了一个死循环,谁也无法继续吃饭。。。

    在计算机系统中也存在类似的情况。例如,某计算机系统中只有一台打印机和一台输入 设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。

    二、死锁产生的原因

    1) 系统资源的竞争

    通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。

    2) 进程推进顺序非法

    进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞。

    信号量使用不当也会造成死锁。进程间彼此相互等待对方发来的消息,结果也会使得这 些进程间无法继续向前推进。例如,进程A等待进程B发的消息,进程B又在等待进程A 发的消息,可以看出进程A和B不是因为竞争同一资源,而是在等待对方的资源导致死锁。

    3) 死锁产生的必要条件

    产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。

    互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

    不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

    请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

    循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, ..., pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的资源被P0占有,如图2-15所示。

    直观上看,循环等待条件似乎和死锁的定义一样,其实不然。按死锁定义构成等待环所 要求的条件更严,它要求Pi等待的资源必须由P(i+1)来满足,而循环等待条件则无此限制。 例如,系统中有两台输出设备,P0占有一台,PK占有另一台,且K不属于集合{0, 1, ..., n}。

    Pn等待一台输出设备,它可以从P0获得,也可能从PK获得。因此,虽然Pn、P0和其他 一些进程形成了循环等待圈,但PK不在圈内,若PK释放了输出设备,则可打破循环等待, 如图2-16所示。因此循环等待只是死锁的必要条件。

    4da3b1b96e85a729e467e5e93d01add8.png

    资源分配图含圈而系统又不一定有死锁的原因是同类资源数大于1。但若系统中每类资 源都只有一个资源,则资源分配图含圈就变成了系统出现死锁的充分必要条件。

    产生死锁的一个例子

    /**

    * 一个简单的死锁类

    * 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒

    * 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒

    * td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;

    * td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;

    * td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。

    */

    public class DeadLock implements Runnable {

    public int flag = 1;

    //静态对象是类的所有对象共享的

    private static Object o1 = new Object(), o2 = new Object();

    @Override

    public void run() {

    System.out.println("flag=" + flag);

    if (flag == 1) {

    synchronized (o1) {

    try {

    Thread.sleep(500);

    } catch (Exception e) {

    e.printStackTrace();

    }

    synchronized (o2) {

    System.out.println("1");

    }

    }

    }

    if (flag == 0) {

    synchronized (o2) {

    try {

    Thread.sleep(500);

    } catch (Exception e) {

    e.printStackTrace();

    }

    synchronized (o1) {

    System.out.println("0");

    }

    }

    }

    }

    public static void main(String[] args) {

    DeadLock td1 = new DeadLock();

    DeadLock td2 = new DeadLock();

    td1.flag = 1;

    td2.flag = 0;

    //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。

    //td2的run()可能在td1的run()之前运行

    new Thread(td1).start();

    new Thread(td2).start();

    }

    }

    三、如何避免死锁

    在有些情况下死锁是可以避免的。三种用于避免死锁的技术:

    加锁顺序(线程按照一定的顺序加锁)

    加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

    死锁检测

    加锁顺序

    当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。

    如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。看下面这个例子:

    Thread 1:

    lock A

    lock B

    Thread 2:

    wait for A

    lock C (when A locked)

    Thread 3:

    wait for A

    wait for B

    wait for C

    如果一个线程(比如线程3)需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。

    例如,线程2和线程3只有在获取了锁A之后才能尝试获取锁C(译者注:获取锁A是获取锁C的必要条件)。因为线程1已经拥有了锁A,所以线程2和3需要一直等到锁A被释放。然后在它们尝试对B或C加锁之前,必须成功地对A加了锁。

    按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁(译者注:并对这些锁做适当的排序),但总有些时候是无法预知的。

    加锁时限

    另外一个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行(译者注:加锁超时后可以先继续运行干点其它事情,再回头来重复之前加锁的逻辑)。

    以下是一个例子,展示了两个线程以不同的顺序尝试获取相同的两个锁,在发生超时后回退并重试的场景:

    Thread 1 locks A

    Thread 2 locks B

    Thread 1 attempts to lock B but is blocked

    Thread 2 attempts to lock A but is blocked

    Thread 1's lock attempt on B times out

    Thread 1 backs up and releases A as well

    Thread 1 waits randomly (e.g. 257 millis) before retrying.

    Thread 2's lock attempt on A times out

    Thread 2 backs up and releases B as well

    Thread 2 waits randomly (e.g. 43 millis) before retrying.

    在上面的例子中,线程2比线程1早200毫秒进行重试加锁,因此它可以先成功地获取到两个锁。这时,线程1尝试获取锁A并且处于等待状态。当线程2结束时,线程1也可以顺利的获得这两个锁(除非线程2或者其它线程在线程1成功获得两个锁之前又获得其中的一些锁)。

    需要注意的是,由于存在锁的超时,所以我们不能认为这种场景就一定是出现了死锁。也可能是因为获得了锁的线程(导致其它线程超时)需要很长的时间去完成它的任务。

    此外,如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。如果只有两个线程,并且重试的超时时间设定为0到500毫秒之间,这种现象可能不会发生,但是如果是10个或20个线程情况就不同了。因为这些线程等待相等的重试时间的概率就高的多(或者非常接近以至于会出现问题)。

    (译者注:超时和重试机制是为了避免在同一时间出现的竞争,但是当线程很多时,其中两个或多个线程的超时时间一样或者接近的可能性就会很大,因此就算出现竞争而导致超时后,由于超时时间一样,它们又会同时开始重试,导致新一轮的竞争,带来了新的问题。)

    这种机制存在一个问题,在Java中不能对synchronized同步块设置超时时间。你需要创建一个自定义锁,或使用Java5中java.util.concurrent包下的工具。写一个自定义锁类不复杂,但超出了本文的内容。后续的Java并发系列会涵盖自定义锁的内容。

    死锁检测

    死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

    每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。

    当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1)。

    当然,死锁一般要比两个线程互相持有对方的锁这种情况要复杂的多。线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着。这是它就知道发生了死锁。

    下面是一幅关于四个线程(A,B,C和D)之间锁占有和请求的关系图。像这样的数据结构就可以被用来检测死锁。

    438a67763ac0f6a7d4e9809ce079597f.png

    那么当检测出死锁时,这些线程该做些什么呢?

    一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁(编者注:原因同超时类似,不能从根本上减轻竞争)。

    一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    展开全文
  • 一、死锁的定义多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都...

    一、死锁的定义

    多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

    下面我们通过一些实例来说明死锁现象。

    先看生活中的一个实例,2个人一起吃饭但是只有一双筷子,2人轮流吃(同时拥有2只筷子才能吃)。某一个时候,一个拿了左筷子,一人拿了右筷子,2个人都同时占用一个资源,等待另一个资源,这个时候甲在等待乙吃完并释放它占有的筷子,同理,乙也在等待甲吃完并释放它占有的筷子,这样就陷入了一个死循环,谁也无法继续吃饭。。。

    在计算机系统中也存在类似的情况。例如,某计算机系统中只有一台打印机和一台输入 设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。

    二、死锁产生的原因

    1) 系统资源的竞争

    通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。

    2) 进程推进顺序非法

    进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞。

    信号量使用不当也会造成死锁。进程间彼此相互等待对方发来的消息,结果也会使得这 些进程间无法继续向前推进。例如,进程A等待进程B发的消息,进程B又在等待进程A 发的消息,可以看出进程A和B不是因为竞争同一资源,而是在等待对方的资源导致死锁。

    3) 死锁产生的必要条件

    产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。

    • 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

    • 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

    • 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

    • 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, ..., pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的资源被P0占有,如图2-15所示。

    直观上看,循环等待条件似乎和死锁的定义一样,其实不然。按死锁定义构成等待环所 要求的条件更严,它要求Pi等待的资源必须由P(i+1)来满足,而循环等待条件则无此限制。 例如,系统中有两台输出设备,P0占有一台,PK占有另一台,且K不属于集合{0, 1, ..., n}。

    Pn等待一台输出设备,它可以从P0获得,也可能从PK获得。因此,虽然Pn、P0和其他 一些进程形成了循环等待圈,但PK不在圈内,若PK释放了输出设备,则可打破循环等待, 如图2-16所示。因此循环等待只是死锁的必要条件。

    30bbc45ca8d19ee50baf6ec1e38580ad.png

    资源分配图含圈而系统又不一定有死锁的原因是同类资源数大于1。但若系统中每类资 源都只有一个资源,则资源分配图含圈就变成了系统出现死锁的充分必要条件。

    产生死锁的一个例子

    /**

    * 一个简单的死锁类

    * 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒

    * 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒

    * td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;

    * td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;

    * td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。

    */

    public class DeadLock implements Runnable {

    public int flag = 1;

    //静态对象是类的所有对象共享的

    private static Object o1 = new Object(), o2 = new Object();

    @Override

    public void run() {

    System.out.println("flag=" + flag);

    if (flag == 1) {

    synchronized (o1) {

    try {

    Thread.sleep(500);

    } catch (Exception e) {

    e.printStackTrace();

    }

    synchronized (o2) {

    System.out.println("1");

    }

    }

    }

    if (flag == 0) {

    synchronized (o2) {

    try {

    Thread.sleep(500);

    } catch (Exception e) {

    e.printStackTrace();

    }

    synchronized (o1) {

    System.out.println("0");

    }

    }

    }

    }

    public static void main(String[] args) {

    DeadLock td1 = new DeadLock();

    DeadLock td2 = new DeadLock();

    td1.flag = 1;

    td2.flag = 0;

    //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。

    //td2的run()可能在td1的run()之前运行

    new Thread(td1).start();

    new Thread(td2).start();

    }

    }

    三、如何避免死锁

    在有些情况下死锁是可以避免的。三种用于避免死锁的技术:

    1. 加锁顺序(线程按照一定的顺序加锁)

    2. 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

    3. 死锁检测

    加锁顺序

    当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。

    如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。看下面这个例子:

    Thread 1:

    lock A

    lock B

    Thread 2:

    wait for A

    lock C (when A locked)

    Thread 3:

    wait for A

    wait for B

    wait for C

    如果一个线程(比如线程3)需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。

    例如,线程2和线程3只有在获取了锁A之后才能尝试获取锁C(译者注:获取锁A是获取锁C的必要条件)。因为线程1已经拥有了锁A,所以线程2和3需要一直等到锁A被释放。然后在它们尝试对B或C加锁之前,必须成功地对A加了锁。

    按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁(译者注:并对这些锁做适当的排序),但总有些时候是无法预知的。

    加锁时限

    另外一个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行(译者注:加锁超时后可以先继续运行干点其它事情,再回头来重复之前加锁的逻辑)。

    以下是一个例子,展示了两个线程以不同的顺序尝试获取相同的两个锁,在发生超时后回退并重试的场景:

    Thread 1 locks A

    Thread 2 locks B

    Thread 1 attempts to lock B but is blocked

    Thread 2 attempts to lock A but is blocked

    Thread 1's lock attempt on B times out

    Thread 1 backs up and releases A as well

    Thread 1 waits randomly (e.g. 257 millis) before retrying.

    Thread 2's lock attempt on A times out

    Thread 2 backs up and releases B as well

    Thread 2 waits randomly (e.g. 43 millis) before retrying.

    在上面的例子中,线程2比线程1早200毫秒进行重试加锁,因此它可以先成功地获取到两个锁。这时,线程1尝试获取锁A并且处于等待状态。当线程2结束时,线程1也可以顺利的获得这两个锁(除非线程2或者其它线程在线程1成功获得两个锁之前又获得其中的一些锁)。

    需要注意的是,由于存在锁的超时,所以我们不能认为这种场景就一定是出现了死锁。也可能是因为获得了锁的线程(导致其它线程超时)需要很长的时间去完成它的任务。

    此外,如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。如果只有两个线程,并且重试的超时时间设定为0到500毫秒之间,这种现象可能不会发生,但是如果是10个或20个线程情况就不同了。因为这些线程等待相等的重试时间的概率就高的多(或者非常接近以至于会出现问题)。

    (译者注:超时和重试机制是为了避免在同一时间出现的竞争,但是当线程很多时,其中两个或多个线程的超时时间一样或者接近的可能性就会很大,因此就算出现竞争而导致超时后,由于超时时间一样,它们又会同时开始重试,导致新一轮的竞争,带来了新的问题。)

    这种机制存在一个问题,在Java中不能对synchronized同步块设置超时时间。你需要创建一个自定义锁,或使用Java5中java.util.concurrent包下的工具。写一个自定义锁类不复杂,但超出了本文的内容。后续的Java并发系列会涵盖自定义锁的内容。

    死锁检测

    死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

    每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。

    当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1)。

    当然,死锁一般要比两个线程互相持有对方的锁这种情况要复杂的多。线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着。这是它就知道发生了死锁。

    下面是一幅关于四个线程(A,B,C和D)之间锁占有和请求的关系图。像这样的数据结构就可以被用来检测死锁。

    d6303a94803b6ddfea3840e93dbde026.png

    那么当检测出死锁时,这些线程该做些什么呢?

    一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁(编者注:原因同超时类似,不能从根本上减轻竞争)。

    一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。

    以上这篇多线程死锁的产生以及如何避免死锁方法(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

    展开全文
  • 一、什么情况会造成死锁呢?...二、如何避免死锁呢? 方法很简单,加锁的时候只要按照锁的顺序进行加锁即可避免产生死锁情况 三、demo死锁情况 #include <stdio.h> #include <pthread.h> int data = 0

    一、什么情况会造成死锁呢?

    由于多把互斥锁的使用不当,导致多个线程无法向前运行,以致死锁。
    如下例子:线程A按先加锁1再加锁2的顺序加锁,线程B按先加锁2再加锁1的顺序加锁,线程A和B都无法获得第二把锁,导致线程A与B甚至其他想获得锁的线程都会被阻塞而无法向前运行

    在这里插入图片描述

    二、如何避免死锁呢?

    方法很简单,加锁的时候只要按照锁的顺序进行加锁即可避免产生死锁情况
    在这里插入图片描述

    三、demo死锁示例

    #include <stdio.h>
    #include <pthread.h>
    
    int data = 0;
    pthread_mutex_t mutex1;
    pthread_mutex_t mutex2;
    
    void *func1(void *arg)
    {
            int n = 3;
    
            pthread_mutex_lock(&mutex1);
            sleep(1);
            pthread_mutex_lock(&mutex2);
            while(n--) {
                    printf("func1: data = %d\n", data++);
                    sleep(1);
            }
            pthread_mutex_unlock(&mutex1);
            pthread_mutex_unlock(&mutex2);
    }
    
    void *func2(void *arg)
    {
            int n = 3;
    
            pthread_mutex_lock(&mutex2);
            sleep(1);
            pthread_mutex_lock(&mutex1);
            while(n--) {
                            printf("func2: data = %d\n", data++);
                            sleep(1);
            }
            pthread_mutex_unlock(&mutex1);
            pthread_mutex_unlock(&mutex2);
    }
    
    int main()
    {
            int ret = 0;
            pthread_t t1;
            pthread_t t2;
    
            pthread_mutex_init(&mutex1, NULL);
            pthread_create(&t1, NULL, func1, NULL);
            pthread_join(t1, NULL);
            pthread_join(t2, NULL);
            pthread_mutex_destroy(&mutex1);
            pthread_mutex_destroy(&mutex2);
    
            return 0;
    }                                                                              
    

    运行结果终端无输出,可见线程1和线程2都无法获得第二把锁导致死锁

    展开全文
  • 本文主要讲java多线程的一些入门知识,包括线程的创建、调度、优先级、等待、休眠、中断及守护线程等...,在讲之前让我们先建立一个“数据字典”以简单了解多线程中出现的一些名词。??????????进程:进程是系统进行...

    9fab25fcccd5d30ce7e82e2440648996.png

    ????????本文主要讲java多线程的一些入门知识,包括线程的创建、调度、优先级、等待、休眠、中断及守护线程等...,在讲之前让我们先建立一个“数据字典”以简单了解多线程中出现的一些名词。

    ??

    ????????进程:进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。可以简单的理解为运行中的应用程序。

    ????????多进程:允许计算机“同时”运行多个进程(应用程序)以提高CPU的利用率。

    ????????线程:线程是进程中的一个实体,是程序使用CPU的基本单位,也就是说线程是依赖进程而存在的。????????

    ????????多线程:在一个进程中同时有多个线程并发的执行。多线程可以提高应用程序的使用率。java多线程避免死锁

    ????????并行:指两个或两个以上事件(或线程)在同一时刻发生,是真正意义上的不同事件或线程在同一时刻,在不同CPU(多核)上同时执行。

    ????????并发:并发的实质是一个物理CPU(也可以多个物理CPU)在若干道程序(或线程)之间多路复用,并发是对有限物理资源强制行使多用户共享以提高效率。

    ????????在我们没接触多线程之前,我们所学到的都是有关顺序编程的知识。即程序中的所有事物在任意时刻都只能执行一个步骤。?? ??

    ??

    ????????不过java虚拟机(JVM)的启动却是多线程的。在我们每次运行java程序的时候就会有相应的java命令去启动JVM,JVM一启动也就相当于系统启动了一个进程,紧接着该进程就创建了一个主线程用于调用main方法,还创建了一个垃圾回收线程用于回收程序运行中可能出现的垃圾。也就是说JVM的启动至少运行着两个线程。那么我们如何实现自己的线程呢?

    ??

    ef3747848abd8613a3a90891458c3489.png

    ????????Java给我们提供了两种方式实现多线程,一种是继承Thread类,然后该子类应重写Thread类的run方法。例如,用于输出1 -- 10的线程可以写成:

    package com.gk.thread.demo;

    public class MyThread extends Thread{

    @Override

    public void run() {

    for (int x=1; x<=10; x++) {

    System.out.println(x);

    }

    }

    }

    ????????然后就可以创建并启动一个线程:

    MyThread mt = new MyThread();

    mt.start();

    ????????另一种创建线程的方式是实现Runnable接口:

    package com.gk.thread.demo;

    public class MyRunnable implements Runnable {

    @Override

    public void run() {

    for (int x=1; x<=10; x++) {

    System.out.println(x);

    }

    }

    }

    ????????再在创建Thread时将Runnable作为参数来传递并启动:

    Runnable runnable = new MyRunnable();

    Thread th = new Thread(runnable);

    th.start();

    ????????以上两种创建线程的方式都要实现run方法,那么run方法有什么作用呢?原来run方法是用来封装那些要被线程执行的代码的,因为我们的Thread类中可能不止有一个方法,run方法就是用来标记哪些代码将来要被线程执行。不过直接调用run方法也是不起多线程作用的。正如上面示例看到的那样,要启动线程,调用的是Thread类的start方法,然后JVM就会帮我们去调用该线程的run方法。

    本文来自电脑杂谈,转载请注明本文网址:

    http://www.pc-fly.com/a/jisuanjixue/article-76705-1.html

    展开全文
  • Java---多线程死锁

    2021-06-03 06:16:02
    死锁的两种情况:简单的说下单块cpu运行多线程的情况:大家可能平时玩电脑,可以同时挂QQ啊,玩游戏啊,打开文本啊,等等。这里,我们假设是单块cpu。也就是俗称的单核cpu。大家可能会觉得这些软件,这些线程是同时...
  • 图1 从 图1 上可以看出, Thread ID : 125 , Thread ID:139 的线程在DriverRegistry$.register方法的45行BLOCKED住,而 Thread ID:127 线程一直HOLD 在RUNNABLE状态. 而从栈信息上看很可能是在 DriverRegistry$....
  • 死锁是这样一种情形:线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不能正常运行.形象的说就是:一个宝藏需要两把钥匙来打开,同时间正好来了两个人,他们一人一把...
  • 线程(英语:thread)是操作系统能够进行运算调度的最小单位。并发:指的是任务数比cpu核数的时候,通过内核调度,...死锁线程获取个锁,造成A需要B持有的锁,B需要A持有的锁。防止死锁: 程序设计时要尽量...
  • 线程访问共享资源时,需要加锁,如果锁使用不当,就会造成死锁这种现象。线程死锁造成的后果:所有的线程都被阻塞,并且线程的阻塞是无法解开的(因为可以解锁的线程也阻塞了)。 造成死锁的场景主要有如下几...
  • 1. java中导致死锁的原因线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。...
  • 有什么方法可以避免多线程死锁的形成吗?请看下文。一、死锁原因与形成条件:死锁形成的原因:1、系统资源不足;2、进程(线程)推进的顺序不恰当;3、资源分配不当。从编程经验上来讲,形成死锁的一般原因有以下几种...
  • 1. 多线程编程与线程安全相关重要概念 开始之前,我们熟悉了几个特别重要的概念:GIL,线程,进程, 线程安全,原子操作。 GIL: Global Interpreter Lock,全局解释器锁。 Cpython解释器上的一把互斥锁,不能利用...
  • 三种用于避免死锁的技术:加锁顺序(线程按照一定的顺序加锁)加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)死锁检测加锁顺序当线程需要相同的一些锁,但是按照...
  • 一、什么情况下会出现死锁假设有两个线程T1,T2。两个账户A,B。刚好某一个时刻在T1线程内A给B转账,在T2线程内B给A转账。代码如下:public static void main(String[] args) throws InterruptedException {Account a...
  • 这篇文章主要介绍了Java线程死锁实例及解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下1、死锁的定义所谓死锁是指线程因竞争资源而造成的一种僵局...
  • qq_407283393122018-12-10一个很蠢的造成死锁的问题wanglt3113172018-12-12什么是死锁死锁的原因,如何避免apanying902019-01-09c3p0连接死锁Cain_1507662016-09-20notify产生死锁的场景(备忘)liuchuanyangyan...
  • 我正在寻找一个从Java 6应用程序中处理数据库死锁的好策略;可能会有几个并行线程同时写入同一个表.如果数据库(Ingres RDMBS)检测到死锁,它将随机杀死其中一个会话.考虑到以下要求,处理死锁情况的可接受技术是什么?&...
  • 线程死锁描述的是这样⼀种情况:线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放。由于线程被⽆限期地阻塞,因此程序不可能正常终⽌。假如线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请...
  • 线程互相等待对方而导致永远暂停称为死锁。常见的死锁q情况是线程A持有锁L1的情况下申请锁L2,线程B持有锁L2申请锁L1。线程A等待锁L2释放,线程B等待锁L1释放,从而互相等待永远暂停。死锁的必要条件1. 资源互斥,...
  • 多线程以改善了系统资源的利用率并且提高了系统的处理能力。但是,并发执行同时也带来了新的问题——死锁。所谓的死锁就是多个线程因竞争资源而造成的一种互相等待,如果没有外力作用,这些线程都将无法继续执行死锁...
  • 生活中的死锁 死锁是什么呢,用生活中的例子来解释: 小明有钱需要买一个二手电动车 小红有二手电动车要卖钱 小明说:先把电动车给我,我就给你钱 小红说:你先给我钱,我给你的电动车 小明拥有钱,但是不拥有车 ...
  • 一、死锁定义1、生活中的列子两人吃饭,但只有一双筷子,2人轮流吃(同时拥有2只筷子才能吃),...这就是死锁2、定义指多线程因竞争资源而造成的一种僵局(互相等待)若无外力作用这些进程都将无法向前推进 。二、死锁...
  • Java程序基本都要涉及到多线程,而在多线程环境中不可避免的要遇到线程死锁的问题。Java不像数据库那么能够检测到死锁,然后进行处理,Java中的死锁问题,只能通过程序员自己写代码时避免引入死锁的可能性来解决。1....
  • 什么是线程死锁死锁是指两个或两个以上的进程(线程)...线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。如下图所示,线程 A 持有资源 2,线程 ...
  • java多线程死锁

    2021-03-08 02:52:19
    进程(线程)同步的基本概念进程之间的制约关系1. 直接制约关系(进程同步)这个关系主要源于进程合作,例如,有一个输入进程A通过单缓冲向进程B提供数据,当该缓冲空时,进程B因为不能获得所需数据而被阻塞,A将数据...
  • 文章目录线程死锁的现象线程死锁的四个条件避免死锁方法 线程死锁的现象 两个或者两个以上的线程在执行过程中,因资源争夺而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直互相等待而无法继续运行...
  • 避免死锁的问题是流行的Java面试问题之一,即使这个问题看起来非常基础,但一旦你开始深入研究,大多数Java开发人员都会陷入困境。那么,什么是死锁?答案很简单 - 当两个或线程正在等待彼此释放所需的资源并...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 113,593
精华内容 45,437
关键字:

多线程避免死锁的方法