Java多线程——Thread和Runnable

Thread和Runnable

  • Thread和Runnable是什么?
  • 线程使用
    • 继承Thread
    • 匿名Thread
    • 引入继承Runnable的继承Thread
    • 引入匿名Runnable的继承Thread
    • 引入继承Runnable的匿名Thread
    • 引入匿名Runnable的匿名Thread
  • 该使用何种方式开启线程?
  • run()、start()和sleep()
  • 线程终止
  • 线程中断
  • 线程状态
  • 线程优先级
  • 守护线程
  • 线程的未捕获异常处理器

Thread和Runnable是什么?

Thread是用于创建线程的类,实现了Runnable接口,会在内部创建线程处理run()方法的逻辑

Runnable是函数式接口,内部只有一个抽象run()方法,可传递Lambda表达式,将任务和线程的操作分离开来,实现代码的解耦合,其源码为

public interface Runnable {
	public abstract void run();
}

线程使用

继承Thread

继承Thread将业务代码放在重写的run()方法中

public class MyThread extends Thread{
    @Override
    public void run() {

    }
}

使用时调用start()方法

Thread myThread = new MyThread();
myThread.start();

匿名Thread

当线程只需要使用一次时,可用匿名对象

new Thread(){
    @Override
    public void run() {
        
    }
}.start();

引入继承Runnable的继承Thread

继承的Thread需要有带Runnable参数的构造函数

public class MyThread extends Thread{
    public MyThread(@Nullable Runnable target) {
        super(target);
    }
}

实现Runnable

public class MyRunnable implements Runnable {
    @Override
    public void run() {

    }
}

调用时

Runnable myRunnable = new MyRunnable();
Thread myThread = new MyThread(myRunnable);
myThread.start();

引入匿名Runnable的继承Thread

继承的Thread需要有带Runnable参数的构造函数

public class MyThread extends Thread{
    public MyThread(@Nullable Runnable target) {
        super(target);
    }
}

调用时

Thread myThread = new MyThread(new Runnable() {
    @Override
    public void run() {
        
    }
});
myThread.start();

引入继承Runnable的匿名Thread

实现Runnable

public class MyRunnable implements Runnable {
    @Override
    public void run() {

    }
}

调用时

MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();

引入匿名Runnable的匿名Thread

在大多数情况下,Runnable和Thread都是只用一次,此时两者都可采用匿名方式

new Thread(new Runnable() {
    @Override
    public void run() {
    		
    }
}).start();

该使用何种方式开启线程?

推荐使用引入Runnable的4种方式

  • 因为Java是单继承,继承了Thread类就不能在继承别的类
  • 引用Runnable可将线程操作和任务操作分离
  • 当对象只使用一次时可使用匿名对象,减少代码量

run()、start()和sleep()

  • run()方法:执行同一线程中的任务,而不会启动新线程

  • start()方法:创建一个执行run()方法的新线程

  • sleep()方法:暂停当前线程的活动

线程终止

线程终止的三个条件:

  • 线程的run()方法执行完最后一条语句
  • 执行return语句
  • 发生未捕获的异常

线程中断

线程的interrupt()方法可中断线程,调用后将置位线程中断状态

Thread.currentThread().interrupt();

清空线程中断状态则调用interrupted(),其返回值为原来中断状态

Thread.interrupted();

判断中断状态调用isInterrupted(),如果线程被阻塞,则无法检测中断状态,此时调用会出现Interrupted Exception,中断不等于终止,线程可以选择如何响应中断

while (!Thread.currentThread().isInterrupted()){
            
}

线程状态

  • New(新创建):当使用new Thread()创建线程,但该线程还未运行时
  • Runnable(可运行):当调用start()方法时,线程处于runnable状态,此时线程可能正在运行,也可能没有运行(如当前线程时间片用完,会被中断从而调用其他线程)
  • Blocked(被阻塞):当线程获取一个内部的对象锁,而该锁被其他线程持有
  • Waiting(等待):当线程等待另一个线程通知调度器一个条件时,自身进入等待状态
  • Timed waiting(计时等待):当调用Thread.sleep、Object.wait、Thread.join、Lock.tryLock和Condition.await进行计时
  • Terminated(被终止):run()方法正常退出或出现了未捕获的异常

线程优先级

可通过setPriority()设置[1,10]的优先级,默认为5(子线程会继承父线程优先级)

Thread.currentThread().setPriority(10);

不要设置过多高优先级的线程,其可能会导致低优先级的线程永远无法执行

守护线程

可通过setDaemon()将当前线程转为守护线程,守护线程为其他线程服务,当只剩下守护线程时,会退出程序

Thread.currentThread().setDaemon(true);

不要用守护线程去访问固定资源(文件、数据库),因为其会在任意时刻中断

线程的未捕获异常处理器

当线程抛出异常且未捕获时,线程会终止,此时异常会传递到一个未捕获异常处理器,可对其指定

Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
    
    }
});

还可对所有线程指定未捕获异常处理器

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
        
    }
});

独立线程的未捕获异常处理器默认未该线程的ThreadGroup对象

你可能感兴趣的:(#,java多线程,java,jvm,开发语言)