黑马程序员--java线程总结(二)

来源:互联网 发布:Ubuntu安装之前分区 编辑:程序博客网 时间:2024/04/29 10:42

----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------



三、线程的同步机制

   

    当多个线程运行时可能操作系统的共享资源,从而会出现无法预料的结果。为了解决竞争共享资源的问题,java用同步机制解决了上述问题,做法是在代表原子操作的程序代码前加上synchronized标记,这样的代码被称为同步代码快。
    每个java对象都有一个同步锁,在任何时刻,只允许一个线程拥有这把锁。当线程试图执行带有synchronized(this)标记的代码块时,线程必须首先获得this关键字引用的对象的锁。然后此时会出现两种情况:
    ①这个锁已经被占用了,java虚拟机会把这个线程放入this所指对象的锁池中,从而线程进入阻塞状态,在此对象的锁池中还可能有许多等待这把锁的线程。等其他线程释放了锁,java虚拟机会从这个池中随机取出一个线程,使这个线程拥有这把锁,并且转到就绪状态。
    ②假如这个锁没有被其他线程占用,线程就会获得这把锁,并且转到就绪状态并且执行。
    具体做法是方法前加上synchronized修饰。
    看以下代码:
public synchronized String A(){ //方法}

public String A(){ synchronized(this){  //方法 }}

这两种情况是等价的。


线程同步的特征有以下几点:
    ①当一个同步代码块和非同步代码快同时操作共享资源的时候,仍然会造成对共享资源的竞争。因为当一个线程执行一个对象的同步代码快时,其他线程仍可以执行此对象的非同步代码快。
    ②每个对象都有唯一的同步锁。
    ③在静态方法前也可以使用synchronized修饰符。
    ④当一个线程开始执行同步代码块时,并不必须以不中断的方式运行。进入同步代码快的线程也可以执行Thread.sleep()或者执行Thread.yield()方法。此时它们并没有释放锁,只是没有运行而是让给了其他线程。
    ⑤synchronized不会被继承。如果一个用synchronized修饰的方法被子类重写,那么子类中这个方法不会再同步,除非也用synchronized修饰。


什么时候释放对象的锁呢?

    ①执行完同步代码块时,就会释放锁。
    ②在执行同步代码块的过程中,抛出异常而导致线程终止,锁会被释放。
    ③在执行同步代码块的过程中,执行锁所属对象的wait()方法时,这个线程会释放锁,并进入对象的等待池中。

什么时候就不释放了呢?

    ①在执行同步代码块的过程中,遇到了Thread.sleep()方法,当前线程就开始睡眠,但是在睡眠过程中不会释放锁。
    ②在执行同步代码块的过程中,执行了Thread.yield()方法时,当前线程放弃cpu,但不会释放锁。

还有一种情况:死锁!
    当许多线程彼此等待对象锁时,造成循环等待时,将发生死锁的现象但是java VM却不去检测更不去避免死锁的发生,所以我们要人工避免。最著名的问题就是哲学家就餐的问题,我将在以后的博客中探讨。

四、sleep()与wait()方法的区别
   

    sleep()是Thread类的方法,是线程用来控制自身的,比如有一个输出时间的线程,每秒打印出一个时间点,那么就要在print()方法前面加上一个sleep()让自己每隔一秒执行一次。 sleep() 指示当前线程暂停执行的时间,把执行的机会让给其他线程,但是状态依然保持,时间到后会自动恢复。调用sleep()不会释放对象锁。

    wait()是Object类的方法,用来线程间的互相通信,当前拥有对象锁的线程等待,直到其他线程调用notify()方法时再醒来,也可指定具体时间。这个方法主要是用在不同线程之间的调度。对象调用wait()方法使本线程放弃对象锁,进入等待此对象的等待锁定池,只有等到发出notify()方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。


以上为本人学习所总结,有不足之处还望大家指正。







0 0