美文网首页
Java线程同步机制

Java线程同步机制

作者: L千年老妖 | 来源:发表于2018-10-07 16:14 被阅读0次

    引出

    一段同步的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池的等待队列中)。取到锁后,他就开始执行同步代码;线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。

    线程状态变换

    下面是线程同步的具体步骤
    线程状态转换图:


    线程同步.png

    Java中的线程的生命周期大体可分为5种状态:

    1.新建(NEW):新创建了一个线程对象。实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态
    2.可运行(RUNNABLE):线程对象创建后,该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权。
    可运行状态只是说你资格运行,调度程序没有挑选到你,你就永远是可运行状态。有下面几种方法转换到可运行状态:
    (一)调用线程的start()方法,此线程进入可运行状态。
    (二)当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态。
    (三)当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入可运行状态。
    (四)锁池里的线程拿到对象锁后,进入可运行状态。
    3.运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice),执行程序代码。
    线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。

    1. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
      (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
      (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
      (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
    2. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

    锁池

    • 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入锁池状态。简言之,锁池里面放的都是想争夺对象锁的线程。
    • 当一个线程1被另外一个线程2唤醒时,1线程进入锁池状态,去争夺对象锁。
    • 锁池是在同步的环境下才有的概念,一个对象对应一个锁池

    线程睡眠的方法比较

    当想暂时停止线程运行时,有下面几种方法:

    1. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入阻塞,但不释放对象锁,millis后线程自动苏醒进入可运行状态。作用:给其它线程执行机会的最佳方式。
    2. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。
    3. t.join()/t.join(long millis),当前线程里调用其它线程1的join方法,当前线程阻塞,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。
    4. obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout)timeout时间到自动唤醒。
    5. obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。

    终止线程

    Java中停止一个线程有两种方法,分别是stop,interrupt,依次来看一下这两种方法。

    stop()
    首先不推荐使用stop方法:

    thread.stop()是不安全的。它的不安全主要是针对于在停止该线程时会释放该线程所持有的所有的锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。

    interrupt() 中断本线程。

    本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
    如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
    如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
    如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
    中断一个“已终止的线程”不会产生任何操作。

    相关文章

      网友评论

          本文标题:Java线程同步机制

          本文链接:https://www.haomeiwen.com/subject/ddblaftx.html