image.png

创建线程的两种方式:

一、继承Thread类

  1. 当一个类继承了Thread类,该类就可以当作线程使用
  2. 重写run()方法,写上自己业务代码
  3. Thread类实现了Runnable接口的run方法

举例:
class Cat extends Thread{
int times = 0;
@Override
public void run(){ //重写run,写上自己业务逻辑
//每隔一秒,输出
while(true){
System.out.println(“喵喵,我是小猫咪” + (++times));
try{
Thread.sleep(1000); //休眠1秒,会抛出异常
} catch(InterruptedExeception e){
e.printStackTrace();
}
}
}
//创建Cat对象,可以当作线程使用
Cat cat = new Cat();
cat.start(); //启动线程

二、实现Runnable接口

java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时无法再继承Thread类
举例:
class Dog implements Runnable{ //通过实现Runnable接口,开发线程
int count = 0;
@Override
public void run(){
//每隔一秒,输出
while(true){
System.out.println(“汪汪…” + (++count) + Thread.currentThread().getName()); //获取线程名称
try{
Thread.sleep(1000); //休眠1秒,会抛出异常
} catch(InterruptedExeception e){
e.printStackTrace();
}
}
}
Dog dog = new Dog();
Thread thread = new Thread(dog);
thread.start(); //启用线程,不能直接调用dog.start(),底层使用了代理模式(静态代理)

image.png
当mian线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行。这时,主线程和子线程交替执行,默认情况下最后一个线程退出了才会退出程序

start()源码

public synchronized void start(){
start0();
}
start0()是本地方法,是JVM调用,底层是c/c++实现
真正实现多线程的效果,是start0(),而不是run
private native void start0();
image.png

继承Thread与实现Runnable区别

  1. 从java的设计来看,通过继承Thread或者实现Runnable来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口 start()-> start0()
  2. 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免单继承的限制

image.png

线程终止

  1. 当线程完成任务后,会自动退出
  2. 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式

run方法中经常是while(loop){},即通过业务逻辑改变loop为false来停止线程

线程常用方法

常用方法第一组

  1. setName //设置线程名称,使之与参数name相同
  2. getname //返回该线程的名称
  3. start //使该线程开始执行; Java虚拟机底层调用该线程的start0方法
  4. run //调用线程对象run方法(run只是个普通方法,直接调用run不会开启线程)
  5. setPriority //更改线程的优先级
  6. getPriority //取线程的优先级
  7. sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
  8. interrupt //中断线程

注意:

  1. 线程优先级范围

image.png

  1. interrupt,中断线程,但并没有真正结束线程 。所以一般用于中断正在休眠的线程

举例:interrupt提前中断休眠
try {
System.out.println(Thread.currentThread().getName()+ “休眠中~~”);
Thread.sleep(20000); //休眠20秒
} catch (InterruptedException e) {
//当该线程执行到一个Interrupt方法时,就会catch一个异常,可以加入自己的业务代码
//InterruptedException是捕获到一个中断异常,
System.out.println(Thread.currentThread().getName()+”被interrupt了”);
}

常用方法第二组

  1. yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
  2. join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

image.png
案例:创建一个子线程,每隔15输出hello,输出20次。主线程每隔1秒,输出hi,输出20次。要求两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续。

用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  3. 常见的守护线程:垃圾回收机制

//如果我们希望当main线程结束后,子线程自动结束,只需将子线程设为守护线程即可
//Daemon:守护线程
MyDaemonThread myDaemonThread = new MyDaemonThread();
myDaemonThread.setDaemon(true);
myDaemonThread.start();

线程的生命周期

JDK中用Thread.State枚举表示了线程的几种状态
image.png
有些地方说有七种状态,是把RUNNABLE状态分为了Ready和Running两种状态
image.png