Loading

Java并发编程基础01-线程管理

线程创建、运行和设置

(1)Java中创建线程的主要两种方式

  • 直接继承Thread类,然后重写run()方法。
  • 构建一个实现Runnable接口的类并重写run()方法,然后创建该类的实例对象,并以其作为构造参数去创建Thread类的对象。建议首选这种方法,因为它可以带来更多的扩展性。

(2)Thread类的主要属性

  • ID:该属性存储了每个线程的唯一标识符。
  • Name:该属性存储了线程的名字。
  • Priority:该属性存储了Thread对象的优先级。线程优先级的范围为1~10,其中1表示最低优先级,10表示最高优先级。修改线程优先级不能保证能发生任何事情,仅仅代表一种可能性
  • Status:该属性保存了线程的状态。在Java中,线程有6种状态——Thread.State枚举中定义这些状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED。
NEW:线程已经创建完毕但未开始执行。
RUNNABLE:线程正在JVM中执行。
BLOCKED:线程处于阻塞状态,并且等待获取监视器。
WAITING:线程在等待另一个线程。
TIMED_WAITING:线程等待另一个线程一定的时间。
TERMINATED:线程执行完毕。

(3)每个Java应用程序都至少有一个执行线程。在程序启动时,JVM会自动创建执行线程运行程序的main()方法。

(4)一个Java程序将在所有非守护线程完成后结束。不会去等待守护线程,当所有非守护线程完成,进程结束!

(5)如果一个线程调用System.exit()命令去结束程序,那么所有线程将会终止各自的运

行。

线程中断

(1)使用task.interrupt()方法对线程进行中断【当调用一个线程对象的interrupt()方法时,中断状态属性将修改为true】,但是需要线程代码进行响应。

(2)利用了中断标识状态来进行自我结束的方法,继承Thread,在程序中判断isinterrupted()是否为true,来自行解决线程生命

(3)如果没有继承Thread,也可以在Runnable中使用Thread.interrupted()静态方法来判断

(4)在Thread类中,还有一个静态方法interrupted(),也能用来检测当前线程是否已被中断。isInterrupted()方法不会修改线程的是否中断属性,而interrupted()方法会将中断属性设置为false。

(5)Java提供了InterruptedException异常,可以在检测到线程中断后抛出该异常,并在run()方法中捕获它。【使用异常方式的优势就是指,可以捕获异常,知晓线程是被中断的

(6)当线程结束时,为什么isinterrupted会为false?应该是线程自己结束时会将中断状态复原为false

线程休眠和唤醒

(1)线程休眠的经典方法:Thread类的sleep()方法。该方法接收一个long类型的参数——该参数是线程将要暂停的时长。【静态方法,Thread.sleep()毫秒为单位】

注:当调用sleep()方法时,线程释放CPU资源,停止执行指定的时间。在这段时间里,线程并不消耗CPU时间,因此CPU可以执行其他任务。

(2)自定义休眠时间单位:可以使用TimeUnit枚举元素的sleep()方法。该方法调用当前Thread类的sleep()方法,使当前线程进入休眠。

(3)当线程在休眠中发生中断时,该方法会立即抛出一个InterruptedException异常,而不会等到休眠时间结束。

(4)yield方法也可以做到是线程释放CPU资源,该方法告知JVM当前线程可以为其他任务放弃CPU资源。JVM并不保证一定会响应该请求

在主线程中等待线程执行完成

(1)当调用一个线程对象的join()方法时,发起调用的线程将会暂停,直到线程对象执行结束。注:

  • join()这样做会阻塞主线程,但是好处也是有的,例如初始化任务作为单独线程,则必须等待其结束
  • 先要开始线程,才能用join等待
    图片

(2)join方法会抛出InterruptedException异常,但是触发条件并不是对其进行中断时,在线程join期间,对其进行中断,并不会抛出中断异常!

守护线程的创建和运行

(1)设置守护线程的方法:setDaemon()方法只能在start()方法之前调用,一旦线程开始执行,其daemon状态便不可修改。

(2)通过isDaemon()方法可以检查线程是一个守护线程(此时方法返回true)还是一个非守护线程(此时方法返回为false)。

(3)守护线程通常是一个无限循环程序,最典型的应用是:JVM垃圾回收器;注意,JVM并不会等待守护线程执行完成,当JVM发现程序只有守护线程存在时,会将其杀死,并结束程序。

处理线程中的非受查异常

(1)Java提供了用于处理线程对象中抛出的非检查异常机制,该机制不是类似于try-catch机制,而是一种处理机制。一旦发生非受查异常,会立刻结束线程,有异常处理器就会转入异常处理器执行。

(2)要使用该机制,必须实现一个处理非检查异常的类。该类必须实现UncaughtExceptionHandler接口,并实现接口中声明的uncaughtException()方法。使用thread.setUncaughtExceptionHandler(new ExceptionHandler())方法来设置处理器。

图片

(3)如果线程对象没有配置非受查异常处理器,则JVM会在控制台中打印出异常信息栈,然后结束异常抛出线程的执行。【而配置了非受查异常处理器,也非常鸡肋,只是可以对异常进行处理而已,线程还是会在异常处结束运行】

(4)Thread类中还定义了另一个用于处理非受查异常的方法,即静态方法setDefault-

UncaughtExceptionHandler()。该方法可以为应用中所有线程对象设置默认的非受查异常处理器。

(5)JVM为非受查异常寻找处理器的顺序

1、线程对象的非受查异常处理器。

2、线程组的非受查异常处理器。

3、默认的异常处理器

使用线程本地变量

(1)ThreadLocal类,实现该类的一个实例,并复写initalValue方法

// 原始写法
ThreadLocal<Date> a = new ThreadLocal<Date>() {
    @Override
    protected Date initialValue() {
        return new Date();
    }
};

// 简单写法:
ThreadLocal<Date> a = ThreadLocal.withInitial(() -> new Date());

注:第一次访问线程本地变量时,若与该线程对象关联的属性值不存在,则将会触发
initialValue()方法,它会为该属性赋值并返回初始值。

(2)InheritableThreadLocal类提供了线程本地变量的继承机制

线程分组

(1)一个ThreadGroup对象可以由一组线程对象或者其他ThreadGroup对象组成,形成一个线程的树形结构。

注:

  • ThreadGroup类不是使用组合模式提供组织
  • 如果组1里有其他线程组,则组1会自动屏蔽所有的线程对象,成为线程组的组。
  • 线程组的优先级高于线程对象

(2)获取处理器内核数的方法:

使用Runtime类的availableProcessors()方法[使用Runtime类的静态方法getRuntime()得到当前应用的Runtime对象],可以得到JVM中可用的处理器数。

(3)创建线程组的范式:

创建一个名为MyThreadGroup的类,并继承ThreadGroup类进行扩展。ThreadGroup类没有无参构造器,因此必须声明一个拥有一个参数的构造器。为了处理线程组抛出的异常,还需要重写uncaughtException()方法

public class MyThreadGroup extends ThreadGroup {
   /**
    * Constructor of the class. Calls the parent class constructor
    * 
    * @param name
    */
   public MyThreadGroup(String name) {
      super(name);
   }
   /**
    * Method for process the uncaught exceptions
    */
   @Override
   public void uncaughtException(Thread t, Throwable e) {
      // Prints the name of the Thread
      System.out.printf("The thread %s has thrown an Exception\n", t.getId());
      // Print the stack trace of the exception
      e.printStackTrace(System.out);
      // Interrupt the rest of the threads of the thread group
      System.out.printf("Terminating the rest of the Threads\n");
      interrupt();
   }
}

(4)线程关联线程组:

Thread t = new Thread(threadGroup, task);

(5)将threadgroup中的线程转化为线程数组的方法

Thread[] threads = new Thread[threadGroup.activeCount()];
threadGroup.enumerate(threads);

使用工程创建线程

(1)线程工厂的核心代码:

ThreadFactory接口只有一个名为newThread()的方法。该方法接收一个Runnable对象作为参数,并返回一个Thread对象。实现一个ThreadFactory接口时,必须覆盖newThread()方法。

(2)使用工厂的好处:

  • 记录线程的创建信息
  • 统一管理某一类线程的创建
posted @ 2021-10-08 15:33  Doubest  阅读(134)  评论(0编辑  收藏  举报