-
一、创建线程
-
方法一:直接使用Thread【继承】
-
-
方法二:使用Runnable配合Thread【组合(优于继承)】
- lambda表达式精简代码
-
-
方法三:FutureTask配合Thread
- FutureTask可以接收Callable类型的参数,用来处理有返回值的情况;
- FutureTask可以接收Callable类型的参数,用来处理有返回值的情况;
-
方法四:线程池
-
-
二、线程运行
- 观察状态
- 交替执行,先后不受程序员控制
- 查看进程线程的方法
- windows
- 任务管理器可以查看进程和线程数,也可以杀死进程
- tasklist查看进程,taskkill杀死进程
- linux
- Java
- jps命令查看所有Java进程
- jstack<PID>查看某个Java进程的所有线程状态
- jconsole查看某个Java进程中线程的运行情况
- windows
- 运行原理
- 栈与栈帧
- 上下文切换:cpu 不再执行当前的线程,转而执行另一个线程的代码
- 线程的cpu 时间片用完;
- 垃圾回收时;
- 有更高优先级的线程需要运行时;
- 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock等方法;
- 观察状态
-
三、线程常见方法
- start(): 启动一个新线程,在新的线程运行run方法中的代码。strat方法只是让线程进入就绪状态,里面的代码不一定立刻运行(CPU时间便没分给它)。每个线程的start方法只能调用一次,多次调用会报错IllegalThreadStateException;
- run(): 新线程启动后会调用的方法,当run方法没有被重写时,默认不执行任何操作;【直接调用run方法不会开启新线程 ==> 不会异步】
- join(): 等到线程运行结束;
- 当前线程中调用t1.join(),则当前线程等待t1线程运行结束再继续运行;
- 实现同步;添加参数实现限时同步;
- join(long n): 等到线程运行结束,最多等待n毫秒;
- getId(): 获取线程长整型的id;
- getName(): 获取线程名;
- setString(String): 设置线程名
- getPriority(): 获取线程优先级;
- setPriority(int): 设置线程优先级
- 1-10的整数,默认为5,较大的优先级能提高线程被CPU调度的概率;
- 最终控制还是由任务调度器决定,设置优先级并不能绝对控制线程的调度;
- getState(): 获取线程的状态,NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;
- isInterrupted(): 判断是否被打断,不会清除打断标记;
- isAlive(): 判断线程是否存活
- interrupt(): 打断线程;
- 如果被打断的线程正在sleep/wait/join(处于阻塞状态),会导致被打断的线程抛出InterruptedException,并清除打断标记;
- 如果打断正在运行的线程,则会设置打断标记,线程并不会停下来,需要配合打断标记来决定是否结束运行(通知型);
- 如果打断park线程,也会设置打断标记[LockSupport.park()],如果打断标记已经是true则park会失效 ==> 使park的线程继续向下运行,重新park失效;
- interrupted(): static方法,判断当前线程是否被打断,会清除打断标记;
- currentThread(): static方法,获取当前正在执行的线程;
- sleep(long n): static方法,让当前执行的线程休眠n毫秒,休眠时让出CPU的时间片给其它线程;Thread.sleep(long)调用;
- 使线程从Running进入Timed Waiting状态;
- 其他线程可以使用interrupt()方法打断正在sleep的线程,这时sleep()方法抛出InterruptedException;
- 睡眠结束后的线程不一定会被立刻执行;
- 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性;
- 当出现while(true)时需要使用sleep()方法防止线程空转,一直占用cpu
- yield(): static方法,提示线程调度器让出当前线程对CPU的使用,主要是为了测试和调试;
- 是线程从Running进入Runnable就绪状态,调度器会调度执行其它线程;
- 具体的实现依赖于操作系统的任务调度器;
- 不推荐使用stop(), resume(), suspend()三个方法,容易造成线程死锁;
-
四、守护线程
- 只要其他的非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束;t1.setDaemon(true);
- eg: 垃圾回收线程就是守护线程;Tomcat中的Acceptor和Poller线程都是守护线程;
-
五、线程状态
-
五种状态(从操作系统层面讲)
- 初始状态:近在语言层面创建了线程对象,还未与操作系统线程关联;
- 可运行状态:就绪状态,指线程已被创建,已与操作系统关联,可以由CPU调度执行;
- 运行状态:指获取了CPU时间片运行中的状态,当时间片用完,会从运行状态切换至可运行状态,并进行上下文切换;
- 阻塞状态:调用阻塞api时会进入,线程不会使用cpu,并会使线程上下文切换,等阻塞api操作完毕,由操作系统唤醒阻塞线程,切换至可运行状态,只要不唤醒,调度器就一直不会考虑调度阻塞的线程;
- 终止状态:表示线程已执行完毕,生命周期已结束,不会再转换为其它状态;
-
六种状态(从Java API层面,根据Thread.State枚举)
- NEW: 线程刚被创建,但是还没有调用start()方法;
- RUNNABLE: 包含操作系统层面的运行状态、可运行状态、以及阻塞api操作导致的阻塞状态;
- BLOCKED: Java中的阻塞
- WAITING: Java中的阻塞
- TIMED_WAITING: Java中的阻塞,如sleep()
- TERMINATED: 线程代码运行结束;
-