Java并发编程札记-(一)基础-04Thread详解

来源:互联网 发布:阿里java面试题2016 编辑:程序博客网 时间:2024/06/06 15:42

在Java并发编程札记-(一)基础-02创建线程一文中,讲到了Thread中提供了许多实用的方法。在Java并发编程札记-(一)基础-03线程的生命周期一文中,讲到了线程各个状态之间的转换,其中许多转换都是通过Thread中的方法来完成的。今天就来学习下Thread。

目录

  1. 线程等待与唤醒(wait()、notify()/notifyAll())
  2. 线程让步(yield())
  3. 线程休眠(sleep())
  4. 线程启动(start())
  5. 中断线程(interrupt())
  6. 线程优先级
  7. 线程等待(join())
  8. 守护线程
  9. 其他方法

线程等待与唤醒(wait()、notify()/notifyAll())

Object中有这么几个方法wait(long timeout)、wait(long timeout, int nanos)、wait()、notify()、notifyAll()。

wait()作用是在其他线程调用此对象的notify()方法或notifyAll()方法前,或者其他某个线程中断当前线程前,导致当前线程等待。wait(long timeout)、wait(long timeout, int nanos)作用是在已超过某个实际时间量前,或者其他某个线程中断当前线程前,导致当前线程等待。使用wait方法有个条件:当前线程必须持有对象的锁。执行wait后,当前线程会失去对象的锁,状态变为WAITING或者TIMED_WAITING状态。

notify()在API中的介绍是

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。通过以下三种方法之一,线程可以成为此对象监视器的所有者:
- 通过执行此对象的同步实例方法。
- 通过执行在此对象上进行同步的 synchronized 语句的正文。
- 对于 Class 类型的对象,可以通过执行该类的同步静态方法。
一次只能有一个线程拥有对象的监视器。

简单来说就是notify()可以随机唤醒正在等待的多个线程中的一个。被唤醒的线程并不能马上参与对锁的竞争,必须等调用notify的线程释放锁后才能参与对锁的竞争。而且被唤醒的线程在竞争锁时没有任何优势。被唤醒的线程可以通过下面三种方式获取到锁:

  • 通过执行此对象的同步实例方法。
  • 通过执行在此对象上进行同步的synchronized语句的正文。
  • 对于Class类型的对象,可以通过执行该类的同步静态方法。

同wait方法一样,使用notify方法有个条件:线程必须持有对象的锁。执行notify方法后,线程会继续执行,并不会马上释放对象的锁。所以才有了上文中的“被唤醒的线程并不能马上参与对锁的竞争,必须等调用notify的线程释放锁后才能参与对锁的竞争。”。

notifyAll()与notify()类似,区别是它可以唤醒在此对象监视器上等待的所有线程。

线程让步(yield())

API中对yield()的介绍是可以暂停当前正在执行的线程对象,并执行其他线程。“暂停”代表着让出CPU,但不会释放锁。执行yield()后,当前线程由运行状态变为就绪状态。但不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权,也有可能是当前线程又进入到运行状态继续运行!

yield()与无参的wait()的区别:

  • 执行yield()后,当前线程由运行状态变为就绪状态。执行wait后,当前线程会失去对象的锁,状态变为WAITING状态。
  • 执行yield()后,当前线程不会释放锁。执行wait后,当前线程会释放锁。

线程休眠(sleep())

线程的休眠(暂停执行)与sleep(long millis)和sleep(long millis, int nanos)有关。API中的介绍是sleep(long millis) 方法可以在指定的毫秒数内让当前正在执行的线程休眠;sleep(long millis, int nanos) 可以在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠。该线程不丢失任何监视器的所属权。简单来说就是sleep方法可以使正在执行的线程让出CPU,但不会释放锁。执行sleep方法后,当前线程由运行状态变为TIMED_WAITING状态。

sleep()与有参的wait()的区别是:

  • 执行sleep()后,当前线程不会释放锁。执行有参的wait()后,当前线程会释放锁。

sleep()与yield()的区别是:

  • 执行sleep后,当前线程状态变为TIMED_WAITING状态。执行yield()后,当前线程由运行状态变为WAITING状态。

线程启动

请参考Java并发编程札记-(一)基础-02创建线程一文中关于start()与run方法的部分。

中断线程(interrupt())

interrupt()常常被用来中断处于阻塞状态的线程。

interrupted()与isInterrupted()都可以测试当前线程是否已经中断。区别在于线程的中断状态由interrupted()清除。换句话说,如果连续两次调用interrupted(),则第二次调用将返回false。

线程优先级

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。java 中的线程优先级的范围是1~10,默认的优先级是5。

setPriority(int newPriority)和getPriority()分别用来更改线程的优先级和返回线程的优先级。

线程等待(join())

join()的作用是等待该线程终止,常常被用来让主线程在子线程运行结束之后才能继续运行。如在主线程main中调用了thread.join(),那么主线程会等待thread执行完后才继续执行。join(long millis)、join(long millis , int nanos)功能与join()类似,但限定了等待时间,join(long millis)意味着等待该线程终止的时间最长为millis毫秒,join(long millis , int nanos)意味着等待该线程终止的时间最长为millis毫秒 + nanos 纳秒,超时将主线程将继续执行。join()等价于join(0),即超时为0,意味着要一直等下去。

从源码中可以了解到,join方法的实现依赖于wait方法,所以join方法释放锁。

守护线程

Java中有两种线程:用户线程和守护线程。守护线程是一种特殊的线程,它的作用是为其他线程提供服务。例如GC线程就是守护线程。当正在运行的线程都是守护线程时,Java虚拟机退出,守护线程自动销毁。

setDaemon(boolean on)用于将该线程标记为守护线程或用户线程。该方法必须在启动线程前调用。isDaemon()用于测试该线程是否为守护线程。

其他方法

修饰符&返回类型 方法名 介绍 public static Thread currentThread 返回对当前正在执行的线程对象的引用。 public final boolean isAlive() 测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。 public final void setName(String name) 改变线程名称 public final String getName() 返回该线程的名称。 public final ThreadGroup getThreadGroup() 返回该线程所属的线程组。如果该线程已经终止(停止运行),该方法则返回null。 public static int activeCount() 返回当前线程的线程组中活动线程的数目。 public static int enumerate(Thread[] tarray) 将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中。 public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 public final void checkAccess() 判定当前运行的线程是否有权修改该线程。 public String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。 public ClassLoader getContextClassLoader() 返回该线程的上下文 ClassLoader。 public void setContextClassLoader(ClassLoader cl) 设置该线程的上下文 ClassLoader。 public static boolean holdsLock(Object obj) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 public StackTraceElement[] getStackTrace() 返回一个表示该线程堆栈转储的堆栈跟踪元素数组。 public static Map getAllStackTraces() 返回所有活动线程的堆栈跟踪的一个映射。 public long getId() 返回该线程的标识符。 public Thread.State getState() 返回该线程的状态。 public static void setDefaultUncaughtExceptionHandler
(Thread.UncaughtExceptionHandler eh) 设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。 public static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() 回线程由于未捕获到异常而突然终止时调用的默认处理程序。 public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() 返回该线程由于未捕获到异常而突然终止时调用的处理程序。 public void setUncaughtExceptionHandler
(Thread.UncaughtExceptionHandler eh) 设置该线程由于未捕获到异常而突然终止时调用的处理程序。


本文就讲到这里,想了解更多内容请参考:

  • Java并发编程札记-目录

END.

原创粉丝点击