zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ");
          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)使用工厂的好处:

    • 记录线程的创建信息
    • 统一管理某一类线程的创建
  • 相关阅读:
    POJ3094 UVALive3594 HDU2734 ZOJ2812 Quicksum【进制】
    UVALive5583 UVA562 Dividing coins
    POJ1979 HDU1312 Red and Black【DFS】
    POJ1979 HDU1312 Red and Black【DFS】
    POJ2386 Lake Counting【DFS】
    POJ2386 Lake Counting【DFS】
    HDU4394 Digital Square
    HDU4394 Digital Square
    UVA213 UVALive5152 Message Decoding
    UVA213 UVALive5152 Message Decoding
  • 原文地址:https://www.cnblogs.com/doubest/p/15380045.html
Copyright © 2011-2022 走看看