Tread

Thread

标签(空格分隔): java android


总结在末尾。
操作系统运行一个程序时,为其创建一个进程process
一个进程内可以有多个线程thread,它是操作系统调度的最小单元,拥有各自的计数器、堆栈、局部变量等,可以访问(进程内的)共享内存。

1. 创建线程

有三种方式,分别是

  • 实现Runnable接口
  • 继承Thread并重写run方法
  • 实现Callable,实现call方法,并用FutureTask获取结果

1.1 实现Runnable接口

   class MyRunable implements Runnable {
        @Override
        public void run() {
            //do sometting
//            Thread.currentThread().xxxx;
        }
    }

//调用方法是
 Thread myThread = new Thread(new MyRunable());
        myThread.start();
//如果直接调用MyRunnabe的run方法,就直接在该线程执行

1.2 继承Thread并重写run方法

class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
        }
    }
//调用时
new MyThread().start();

run方法会在新线程执行,注意Thread自身也实现了Runnable

1.3 实现Callable,实现call方法,并用FutureTask获取结果

这种方式执行的线程可以获取结果和响应中断。

    class MyCallable implements Callable{
        @Override
        public String call() throws Exception {
            return null;
        }
    }

        //让线程运行
        FutureTask task = new FutureTask(new MyCallable());
        Thread thread = new Thread(task);
        thread.start();
        //获取结果
        try {
        //会阻塞
            String result = task.get();
        } catch (ExecutionException | InterruptedException e) {

        }

2. 线程的基本属性

优先级、守护线程。

2.1 优先级 priority

操作系统采用时间片(CPU 单次执行某线程的时间)的形式来调度线程的运行,线程被 CPU 调用的时间超过它的时间片后,就会发生线程调度。
优先级影响线程得到的时间片多少。
java中,默认为5,最高10,最低1。

2.2 守护线程

private boolean deamon标识是否为守护线程。
要设置deamon属性,需要在start之前调用。
守护线程用于做一些后台调度、支持性工作,比如垃圾回收、内存管理等。
一个进程中,如果所有线程(不包括守护线程)都退出了,虚拟机就会退出。
守护进程的finally块中的方法不一定被执行,所以其资源清理工作不能放在finally中。

3. 线程的生命周期

线程生命周期

3.1 表格描述

线程状态 介绍 备注
NEW 新创建 还未调用 start() 方法;还不是活着的 (alive)
RUNNABLE 就绪 调用了 start() ,此时线程已经准备好被执行,处于就绪队列;是活着的(alive)
RUNNING(java没有,RUNNABLE替代了) 运行中 线程获得 CPU 资源,正在执行任务;活着的
BLOCKED 阻塞的 线程阻塞于锁或者调用了 sleep;活着的
WAITING 等待中 线程由于某种原因等待其他线程;活着的
TIME_WAITING 超时等待 与 WAITING 的区别是可以在特定时间后自动返回;活着的
TERMINATED 终止 执行完毕或者被其他线程杀死;不是活着的
--- --- ---

3.2 进入Waiting的四种方法:

  • Object.wait();
  • Thread.join();
  • LockSupport.park();
  • Lock.lock();

3.3 判定线程是否存活

public final native boolean isAlive()
除了NEWTERMINITED状态都返回true

4. 线程的关键方法

4.1 sleep

底层是本地方法,通过系统调度暂停当前线程。

  • 阻塞当前线程。如果在主线程调用其他线程的sleep
  • 让出CPU但不释放锁。
public static native void sleep(long millis) throws InterruptedException;

4.2 Object wait

是对象级别的方法,本质是获取、释放ObjectMonitor

  • 调用前要先获取到对象锁。
  • 让出CPU,释放对象锁。
  • 调用后使该线程进入目标对象监视器的等待队列。

4.3 Thread.yield

静态方法,将当前线程切换到就绪状态,让系统重新分配CPU执行时间。
如果分配结果是调用了yield的线程继续执行,那么它就继续执行。

4.4 Thread join

如果线程A执行thread.join,表示线程A等待thread执行终止后才从join返回。
允许提供时间参数,超时后线程A从join中返回。
底层靠wait实现

  //加锁当前线程对象
  public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        //millis不得小于0
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        //如果millis等于0,即没有超时设置
        if (millis == 0) {
            //条件不满足,就继续等待
            while (isAlive()) {
                wait(0);
            }
        } else {
            //如果条件不满足,并且在延迟时间内,就继续等待
            while (isAlive()) {
                //计算延迟时间
                long delay = millis - now;
                //判断是否到了延迟时间,如果到了,直接返回
                if (delay <= 0) {
                    break;
                }
                //继续等待
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

很好懂,如果没到超时时间,当前线程继续wait,到了就break继续运行(调用thread.join的)当前线程。

4.5 线程的中断

thread.wait或者thread.sleep都可以。
同时Thread也提供了设置中断标志的api。

public void interrupt();//设置中断标志位为true,注意只是设置标志位,而不会中断一个正在执行的线程。
public boolean isInterrupted();//返回线程中断标志位
public static boolean interrupted();//返回线程标志位,同时将标志位复位为false
interrupt

改变线程的中断状态,给受阻塞的线程发出中断信号,而不会中断正在运行的线程。

isInterrupted

返回目标线程的中断状态。
当一个线程在sleep或者wait状态被中断,在收到中断请求,抛出InterruptedException之前,JVM会将isInterrupted标志复位。就是说在catch(InterruptedException e)代码块里调用isInterrupted()会返回false

static interrupted

返回当前线程的中断状态并重置为false

使用interrupt的正确姿势。

可以在捕捉到异常时再次设置中断标志位。或者自定义设置标志位,并允许其他线程调用,就可以显式地在其他线程中断。下边是第一种的示例:

          while (!Thread.interrupted()) {
                System.out.println("Runnable " + name + " is running");
                try {
                    Thread.sleep(sleep);
                } catch (InterruptedException e) {
                    System.out.println("Runnable " + name + " is interrupted when sleep");
                    Thread.currentThread().interrupt();
                }
            }
            System.out.println("Runnable " + name + " is interrupted");
被废弃的stop

当调用stop,线程会立即停止,并抛出ThreadDeathError。由于以下两种原因不建议使用它:

  • stop方法是同步的,当run方法也是同步,会导致工作线程的run方法结束之后才stop,时效性受到影响。
  • sop方法线程不安全。执行时会强制释放持有的所有锁,可能引发错误数据。

5. 总结

1.线程用法。实现Runnable、继承Thread、实现Callable,结合Task
2.优先级、守护进程。(这段是总结的不太好,可以看相关文章)。
3.线程的生命周期。
4.线程的中断机制。

参考文章

hapjin-JAVA多线程之中断机制(如何处理中断?)
拭心-并发编程1:全面认识 Thread)
leeon_l-Java中断interrupt详解)

你可能感兴趣的:(Tread)