- 实现Runable接口
- 继承Thread
- 实现Callable接口
public interface Runnable {
//抽象的run方法
public abstract void run();
}
实现Runnable接口,实现其提供的run方法
Runnable实现类使用Demo
//Runnable实现类的使用
RunableDemo runable = new RunableDemo();
Thread thread = new Thread(runable, "A");
//子线程的启动需要调用start方法
thread.start();
使用步骤:
1、创建一个Runnable接口的实现类,并实现run()方法
2、实例化Runnable接口实现类
3、创建Thread类实例,将实例化的Runnable实例作为参数传递
4、启动子线程,调用Thread类的实例的start方法
Thread类的定义:
public class Thread implements Runnable
Thread类也是Runable接口的实现类,实现类run方法
public void run() {
if (target != null) {
target.run();
}
}
继承Thread类时,重写run方法
继承Thread类使用Demo
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
使用步骤:
1、创建类,继承Thread(extends Thread),重写run方法
2、实例化当前创建的Thread类的子类
3、启动子线程,调用start方法
Callable接口的什么如下:
public interface Callable<V> {
V call() throws Exception;
}
Callable接口提供了call方法,具有范围值,通过泛型来定义,该接口可以抛出异常
FutureTask类声明:
public class FutureTask<V> implements RunnableFuture<V>
//RunnableFuture接口声明
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
FutureTask类是继承Runable接口,即FutureTask是Runable接口的实现类,而FutureTask类提供了构造函数FutureTask(Callable callable),可以接受Callable类型的任务
Callable接口的使用Demo:
//实现Callable接口
CallableDemo callableDemo = new CallableDemo();
//通过FutureTask可以将Callable实现类兼容成Runable实现类
FutureTask futureTask = new FutureTask <>(callableDemo);
Thread thread = new Thread(futureTask);
thread.start();
使用步骤:
1、创建类,实现Callable接口,实现其提供的call方法
2、创建Callable实例
3、创建FutureTask实例,将Callable实例作为参数传入
4、创建Thread类实例,将FutureTask实例作为参数传入(当做Runable实例)
5、启动子线程,调用Thread类的start方法
Runable接口和callable接口区别:
1、Callable接口规定的方法是call()方法,Runnable接口规定的方式是run()方法
2、Callable的任务执行后有返回值,而Runable的任务不能有返回值
3、call()方法可抛出异常,run()方法不能抛出异常
4、通过运行Callable的实例可以获取到FutureTask对象,FutureTask对象表示异步执行的结果,他提供了get方法可以异步获取线程执行的结果(get方法会阻塞当前线程),可以了解子线程的执行情况,可以取消任务的执行
Java中线程状态由6种
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
用new创建的线程处于新建状态,此时他和其他的Java对象一样,仅仅在堆中分配了内存
当线程对象被创建后,调用了start方法,线程就处于就绪状态,处于就绪状态的线程(其他的条件都满足,可以等待获取CPU的使用权),等待获取CPU的使用权
处于运行状态的线程也就占用的CPU,执行线程代码。只能有就绪状态的线程进入到运行状态
阻塞状态是指线程因为某些原因放弃CPU(缺少资源:IO、锁),暂时停止运行,当前的线程处于阻塞状态
当线程处于该状态,如果某个线程中的对象的wait()时,JVM就会将线程放入到等待池池
sleep(long time),jion(long time) 会使线程处于睡眠状态
当线程执行到run()方法结尾时,就进入到终止状态,该表示线程的生命周期结束
通过线程转换可知:一个线程的生命周期中需要的状态:New、Runable、Running、Terminated四个状态
线程在需要响应的资源时,进入到阻塞状态:阻塞状态包含Waiting,Blocked、Time_waiting状态
start方法启动一个新线程,start方法首先调用才能创建子线程,不能重复使用
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
boolean started = false;
try {
start0();
started = true;
} finally {
//省略非核心代码
}
}
private native void start0(); //调用native方法
启动方法是需要底层OS来启动子线程,start是间接启动
native方法说明:
JNI:Java native interface,java本地方法接口(JMM中存在本地方法栈),调度系统本身的提供的非Java代码(C/C++代码)
整个子线程执行的业务逻辑都在run方法中
单独调用run方法,会在当前的线程中执行run()操作,和普通方法调用是一样的,不会启动子线程,run方法可以重复调用
public void run() {
if (target != null) {
target.run();
}
}
Thread类中的run方法只是判断任务体Runable是否存在,未做其他业务,继承Thread是需要重写
是用来暂停当前线程的执行,并且让步于其他相同优先级或更高优先级的线程先执行
yield():方法在thread类中,是Thread类中静态方法
yield方法特点:
1、yield方法让步CPU的资源,让给谁由系统决定的,一般是让给相同优先级或者更高优先级的线程获得执行权,如果没有的话,会执行原来的线程
2、yield让步:会让当前线程由“运行状态”进入到“就绪状态”,等待CPU的调度
3、yield让步CPU资源后,线程不会释放锁
public static native void yield();
暂停当前线程执行,等待子线程的执行,也叫做线程合并,join方式是将并行执行的线程合并成串行执行,
例:在线程ta中调用tb.join,会暂停ta的执行,先让tb执行完毕,ta才会执行
方法介绍:
t.join() 允许t线程在当前线程之前执行,待t线程执行结束当前线程在执行
t.join(long millis)(时间单位:毫秒)允许t线程在当前线程之前执行,且最长时间millis毫秒之后,当前线程才能执行
t.join(long millis, int nanos)与t.join(long)一样,只不过可以提供纳秒级的精度
方法特点:
1、join方法是thread类中的方法,会抛出InterruptedException中断异常
2、当前线程ta中tb.join,tb会执行,ta线程会进入WAITING或TIMED_WAITING状态
3、当前线程ta中tb.join,则ta线程会释放当前持有的锁,join方法实现是通过wait/notify线程通信方式来实现的,wait方法的使用会释放锁
//一直等待子线程
public final void join() throws InterruptedException {
join(0);
}
//提供毫秒,纳秒级别的等待时间
public final synchronized void join(long millis, int nanos) throws InterruptedException {
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
//纳秒的处理最终转换为毫秒处理
join(millis);
}
//提供毫秒级别的等待时间
public final synchronized void join(long millis)throws InterruptedException {
if (millis == 0) {
//millis=0,会一直判断子线程是否结束,否则会一直等待
while (isAlive()) {
//线程等待
wait(0);
}
} else {
//判断子线程是否结束且是否到达指定的毫秒数,否则会等待
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
}
}
}
eg:假如存在A、B、C三个线程,让三个线程按照C、B、A的顺序执行
package joinTestDemo;
class Thread1 extends Thread{
public void run(){
System.out.println("Threada");
}
}
class Thread2 extends Thread{
public void run(){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Threadb");
}
}
class Thread3 extends Thread{
public void run(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Threadc");
}
}
public class AbcSort {
public static void main(String[] args) throws InterruptedException {
Thread3 thread3=new Thread3();
thread3.start();
thread3.join();
Thread2 thread2=new Thread2();
thread2.start();
thread2.join();
Thread1 thread1=new Thread1();
thread1.start();
}
}
用来中断当前线程,终止处于“阻塞状态”的线程
方法介绍:
interrupt():该方法在Thread类中,是一个普通方法,由对象调用该方法
boolean isInterrupted() 判断是否发送了中断操作, true:发生中断操作 false:未发生中断操作
方法特点:
1、如果当前线程是可中断的阻塞状态(join、sleep、wait等方法会导致线程进入阻塞撞状态:WATING / TIMED_WAITING状态),在任意其他的线程中调用interruprt方法,那么会立即抛出抛出InterruptedException来停止的阻塞状态
2、如果当前线程是可运行状态。调用interruprt方法,线程还是会继续执行,直到发生了sleep、join、wait等方法的调用,才会在进入阻塞之后,随后立即抛出InterruptedException,跳出阻塞状态
public void interrupt() {
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag 注意这句
b.interrupt(this);
return;
}
}
interrupt0();
}
private native void interrupt0(); //JNI方法,中断操作由系统来提供中断的方式
会让线程休眠,而且那个线程调用,那个线程休眠 ,TimeUtil.sleep(long),Threrd.sleep、或当前的额线程t.sleep,其结果都是当前的线程休眠
方法介绍:
sleep(long millis)
sleep(long millis, int nanos)
都是提供休眠操作,时间单位粒度不同
sleep 是Thread类提供的方法,会抛出InterruptedException异常
方法特点:
1、sleep休眠期间,会让出CPU使用权,但线程任然持有锁
2、sleep休眠时间到了之后,不会立即执行,而是线程由“阻塞状态”进入到“就绪状态”
public static native void sleep(long millis) throws InterruptedException //JNI
public static void sleep(long millis, int nanos) throws InterruptedException {
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}
方法介绍:
setDaemon(boolean on) 设置线程为守护线程 true:表示是守护线程 false:非守护线程 默认false
boolean isDaemon():判断当前线程是否是守护线程 true:守护线程
Java中的线程主要有两种:用户线程和守护线程
守护线程和用户线程是什么?
用户线程一般用户执行的用户级的线程
守护线程:也叫做后台线程,脱离于终端,用来服务于用户线程 例如:GC是一个单独的线程来处理,GC线程就是一个守护线程
守护线程的生命周期?
守护线程的生命周期是依赖于用户线程,当有用户线程存在,守护线程就会存活,当没有用户线程存在,那守护线程也随之消亡,
需要注意的是:Java虚拟机在“用户线程”都结束后会后退出。
public final void setDaemon(boolean on) {
checkAccess();
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
public final boolean isDaemon() {
return daemon;
}
线程优先级,顾名思义:就是来指导线程的执行优先级的
方法介绍:
int getPriority() 获取优先级
setPriority(int newPriority) 设置优先级
方法特点:
1、java线程的优先级并不绝对,它所控制的是执行的机会,也就是说,优先级高的线程执行的概率比较大,而优先级低的线程也并不是没有机会,只是执行的概率相对低一些。
2、Java线程一共有10个优先级,分别为1-10,数值越大,表明优先级越高,一个普通的线程,其优先级为5;
线程的优先级具有继承性,如果一个线程B是在另一个线程A中创建的,则B叫做A的子线程,B的初始优先级与A保持一致。
优先级范围:
public final static int MIN_PRIORITY = 1; //最小优先级
public final static int NORM_PRIORITY = 5; //默认优先级
public final static int MAX_PRIORITY = 10; //最大优先级
java 中的线程优先级的范围是1~10.最小值是1,默认的优先级是5,最大值是10.“高优先级线程”会优先于“低优先级线程”执行
public final int getPriority() {
return priority;
}
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}
private native void setPriority0(int newPriority); //JNI