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<String>{
        @Override
        public String call() throws Exception {
            return null;
        }
    }

        //让线程运行
        FutureTask<String> task = new FutureTask<String>(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详解)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 该文章转自:http://blog.csdn.net/evankaka/article/details/44153...
    加来依蓝阅读 7,395评论 3 87
  • 单任务 单任务的特点是排队执行,也就是同步,就像再cmd输入一条命令后,必须等待这条命令执行完才可以执行下一条命令...
    Steven1997阅读 1,227评论 0 6
  • 下面是我自己收集整理的Java线程相关的面试题,可以用它来好好准备面试。 参考文档:-《Java核心技术 卷一》-...
    阿呆变Geek阅读 14,962评论 14 507
  • 昨天回去有些晚,未曾炒菜,凑合着切了些黄瓜,煮了锅米饭便带去上班。 幸好上次在公司存了一瓶老干妈。老干妈伴米饭,倒...
    化浊阅读 295评论 0 0
  • 今夏的蚊虫不多 乘凉闲谈的时光太惬意 一切仿佛静止 月亮却悄上枝头 猛地抬头 得一片清明 心中庆幸又窃喜 日月星河...
    吴小夕阅读 145评论 1 1