`

转:线程同步和线程死锁

    博客分类:
  • java
阅读更多

饮水思源:原著http://blog.csdn.net/superhill/article/details/7526771

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中去调用它。
分享到:
评论

相关推荐

    Python语言基础:线程同步.pptx

    虽然`Lock()`能有效防止数据竞争,但过度使用线程同步可能导致死锁(Deadlock)、活锁(Livelock)等问题,降低程序的并发性能。因此,在设计多线程程序时,需要合理地使用同步机制,平衡数据一致性与程序效率。 7...

    重大软院操作系统实验三:线程同步

    预防和检测死锁是线程同步的重要部分。Linux内核提供了一些死锁避免策略,如资源预留和银行家算法。 6. **哲学家就餐问题**:这是一个经典的线程同步问题,用来说明死锁的可能性。在实验中,你可以通过实现这个例子...

    Java多线程编程总结

    Java线程:并发协作-死锁 Java线程:volatile关键字 Java线程:新特征-线程池 Java线程:新特征-有返回值的线程 Java线程:新特征-锁(上) Java线程:新特征-锁(下) Java线程:新特征-信号量 Java线程:新特征-...

    C语言多线程编程:线程控制与同步机制详解

    最后,通过生产者消费者问题和读者写者问题的示例,进一步阐释了线程同步的具体应用,以及在实际编程中如何确保线程安全和避免死锁。 适合人群:具有一定C语言编程基础的软件开发人员和学生。 使用场景及目标:帮助...

    JAVA_线程同步与死锁

    一个详细讲解JAVA_线程同步与死锁的例子 希望可以帮助到你。

    linux内核知识系列:线程&进程

    3. **线程同步**:线程同步机制包括互斥锁、条件变量、读写锁、信号量等,防止数据竞争和死锁问题。 4. **线程调度**:线程调度在进程级别进行,同一进程内的线程由内核调度器统一调度,线程切换成本比进程切换低。...

    多线程的批量线程同步解决方案

    线程同步是多线程编程中的重要概念,用于控制不同线程间的执行顺序和访问共享资源的方式,防止竞态条件和死锁的发生。常见的线程同步机制包括: 1. **互斥量(Mutex)**:一种简单的同步机制,一次只有一个线程能获取...

    vc++中的线程锁(线程锁保持线程同步)

    在VC++编程环境中,线程同步是一个至关重要的概念,特别是在多线程程序设计中,以确保并发执行的线程能够安全地访问共享资源,避免数据竞争和其他潜在的问题。本篇文章将详细探讨线程锁在VC++中的应用,以及如何通过...

    java模拟线程死锁

    Java 模拟线程死锁 线程死锁 在 Java 中,线程死锁(Deadlock)是一种特殊的情况,发生...为了避免线程死锁,我们需要在设计线程之间的交互时,遵循一定的规则和原则,例如避免循环等待、使用锁对象和线程通信机制。

    多线程同步和通讯完整示例

    在编程领域,尤其是在Java这样的多线程环境中,理解和掌握多线程同步与通讯至关重要。本文将深入探讨这些概念,以及如何使用synchronized关键字、wait-notify机制和Lock接口来实现线程间的同步与通讯。 首先,多...

    线程同步的四种方式

    在多线程编程中,线程同步是一种控制多个线程并发执行时访问共享资源的方式,以避免数据不一致和死锁等问题。以下是对线程同步的四种主要方式的详细解释: 1. **事件(Event)** 事件是Windows API提供的一种线程...

    线程同步小例子

    在编程领域,线程同步是多线程编程中的一个核心概念,它涉及到如何有效地管理和协调多个并发执行的线程,确保它们能正确地共享资源,避免数据竞争和死锁等问题。这个“线程同步小例子”是基于孙鑫先生著作中的示例...

    线程同步解决火车站售票问题

    4. **死锁**:在实现线程同步时,还需要注意避免死锁的发生,即两个或更多的线程相互等待对方释放资源,导致程序无法继续执行。在设计售票逻辑时,应确保没有循环等待的情况。 5. **公平与非公平策略**:线程同步...

    Java多线程同步.pdf

    Java多线程同步是指在Java语言中,如何使用synchronized关键字和其他同步机制来确保多线程程序的正确执行。在Java语言中,synchronized关键字用于对方法或者代码块进行同步,但是仅仅使用synchronized关键字还不能...

    java线程详解

    七、线程死锁 八、线程同步小结 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:volatile关键字 Java线程:新特征-线程池 一、固定大小的线程池 二、单任务线程池 三、可变尺寸的线程池 四、...

    VC++线程同步实例

    在编程领域,线程同步是多线程编程中的一个重要概念,它确保了多个线程在访问共享资源时的正确性和一致性。在这个“VC++线程同步实例”中,我们将探讨如何利用VC++(Visual C++)来实现线程间的同步,以避免数据竞争...

Global site tag (gtag.js) - Google Analytics