Thread

Thread

  • 1.生命周期
  • 2.构造方法
  • 3.创建线程的方式
  • 4.常用的方法

一个线程就是一个执行流程. 每个线程都可以按照顺序执行自己的代码, 而多个线程可以同时执行多份代码

1.生命周期

Thread_第1张图片
Thread有六个生命周期

  1. 新建(NEW)
    当一个Thread对象被创建时,它处于新建状态。在这个阶段,线程还没有启动。
public static void main(String[] args) throws Exception{
    System.out.println("Thread State is:"+new Thread().getState());
}
  1. 就绪(Runnable)
    当调用线程对象的start()方法后,线程进入就绪状态。在就绪状态下,线程已经准备好运行,但还没有得到 CPU 时间片。
public static void main(String[] args) {
   new Thread(() -> {
        System.out.println("Thread State is:"+Thread.currentThread().getState());
    }).start();
}
  1. 运行(Running)
    线程获得CPU时间片后,进入运行状态。在运行状态下,线程执行具体的任务代码。
  2. 阻塞(Blocked)
    线程在某些情况下会由于某些原因放弃CPU时间片,进入阻塞状态。例如,线程等待某个资源的释放,或者调用了sleep()方法。
public class ThreadTest {

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();//锁
        BlockThread t1 = new BlockThread(lock,"T1");
        BlockThread t2 = new BlockThread(lock,"T2");
        t1.start(); //线程 T1开始运行
        t2.start(); //线程 T2开始运行
        Thread.sleep(100);  //阻塞主线程,等待T1,T2抢锁
        System.out.println("Thread T1 State is " + t1.getState());  //获取T1线程状态
        System.out.println("Thread T2 State is " + t2.getState());  //获取T2线程状态
    }
}

class BlockThread extends Thread {
    private String name;    //当前线程名称
    private Object lock;    //锁

    public BlockThread(Object lock, String name) {
        this.lock = lock;
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("Thread " + name + " State is " + Thread.currentThread().getState());
        synchronized (lock) {
            System.out.println("Thread " + name + " hold the lock");
            try {
                System.out.println("Thread " + name + " State is " + Thread.currentThread().getState());
                Thread.sleep(1000 * 10);    //抢到锁的线程执行逻辑,这里用睡眠模拟
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread " + name + " release the lock");
        }
    }
}
  1. 等待(Waiting)
    线程在等待某个条件的触发时进入等待状态。例如,调用Object.wait()方法或者 Thread.join()方法。
public class ThreadTest {

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        WaitingThread t = new WaitingThread("T", lock);
        t.start();
        Thread.sleep(1000);
        System.out.println("Thread T State is " + t.getState());
        System.out.println("Thread "+Thread.currentThread().getName()+" State is " + Thread.currentThread().getState());
    }
}

class WaitingThread extends Thread {
    private Object lock;
    public WaitingThread(String name, Object lock) {
        super(name);
        this.lock = lock;
    }

    @Override
    public void run() {
        System.out.println("Thread " + Thread.currentThread().getName()+" try to wait");
        synchronized (lock) {
            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 超时等待(Timed Waiting): 类似于等待状态,但有一个超时时间。例如,调用 Thread.sleep() 方法或者带有超时参数的 Object.wait() 方法。
  2. 终止(Terminated)
    线程执行完毕或者因异常退出时,进入终止状态。线程一旦终止,就不能再进入其他状态。

阻塞和等待的区别

阻塞状态 等待状态
原因 当线程试图获得一个对象的锁, 但该锁已经被其他线程持有时, 线程会进入阻塞状态 当线程在某个对象上调用Object.wait()方法时, 它会进入等待状态, 等待期间会释放对象的锁
解除 当持有锁的线程释放锁时, 被阻塞的线程有机会重新竞争锁, 并进入就绪状态 线程可以通过调用Object.notify()或Object.notifyAll()来被唤醒,也可以在等待期间等待超时
是否会释放持有的锁 在阻塞状态下, 线程仍然保持对锁的所有权 在等待状态下, 线程会释放锁, 让其他线程有机会获得锁

阻塞状态结束后, 线程重新进入就绪状态,
在等待状态中, 线程可以被唤醒, 被唤醒的线程将重新进入就绪状态, 并尝试获取对象的锁

2.构造方法

Thread() :创建一个默认设置的线程对象实例
Thread(Runnable target) :创建一个包含可执行对象的线程实例
Thread(Runnable target, String name) :创建一个包含可执行对象,指定名称的线程对象
Thread(String name):创建一个指定名称的线程对象
Thread(ThreadGroup group, Runnable target) :创建一个指定线程组,包含可执行对象的线程对象实例
Thread(ThreadGroup group, Runnable target, String name) :创建一个指定线程组,包含可执行对象,指定线程名称的线程对象实例
Thread(ThreadGroup group, Runnable target, String name, long stackSize) :创建一个指定线程组、包含可执行对象、指定名称以及堆栈大小的线程对象实例
Thread(ThreadGroup group, String name):创建一个指定线程组,线程名称的线程实例

3.创建线程的方式

1.继承Thread类

public class ThreadTest {

    public static void main(String[] args) throws InterruptedException {
        // 继承Thread
        MyThread thread1 = new MyThread();
        thread1.start();

        // 匿名内部类
        Thread thread2 =new Thread() {
            @Override
            public void run() {
                System.out.println("Hello World 2");
            }
        };
        thread2.start();
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
}

2.实现Runnable接口

public class ThreadTest {

    public static void main(String[] args) throws InterruptedException {
        // 继承Thread
        Thread thread1 = new Thread(new MyRunnable());
        thread1.start();

        // 匿名内部类
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World 2");
            }
        });
        thread2.start();
    }
}
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
}

3.实现Callable接口
FutureTask 是一个类,它实现了 Runnable 和 Future 接口

Callable不能直接交给Thread来运行, 我们可以使用FutureTask包装Callable, 让它变成一个Runnable

public class FutureTaskThreadDemo {
    public static void main(String[] args) {
        // 创建一个实现 Callable 接口的匿名类实例,该实例在 call() 方法中返回一个字符串
        Callable<String> callableTask = new Callable<String>() {
            @Override
            public String call() throws Exception {
                // 在这里执行你的任务,并返回结果
                String result = "Hello from Callable!";
                return result;
            }
        };
 
        // 使用 Callable 任务创建一个 FutureTask 实例
        FutureTask<String> futureTask = new FutureTask<>(callableTask);
 
        // 创建一个新线程,并将 FutureTask 作为参数传递给它
        Thread taskThread = new Thread(futureTask);
 
        // 启动新线程,这将执行 FutureTask 中的 Callable 任务
        taskThread.start();
 
        try {
            // 从 FutureTask 对象中获取计算结果,如果任务尚未完成,此方法会阻塞等待
            String result = futureTask.get();
            System.out.println("Result: " + result);
        } catch (InterruptedException e) {
            // 当前线程在等待过程中被中断时抛出 InterruptedException
            e.printStackTrace();
        } catch (ExecutionException e) {
            // 计算任务抛出异常时抛出 ExecutionException
            e.printStackTrace();
        }
    }
}

4.常用的方法

getId() 获取线程的ID
getName() 获取线程的名称
getState() 获取线程的状态
getPriority() 获取线程的优先级
isDaemon() 是否为后台线程
isAlive() 判断线程是否存活
interrupt() 中断线程
isInterrupted() 判断线程是否被中断
join() 让一个线程等待另一个线程执行结束
public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        int count = 5;
        while (count-- != 0) {
            System.out.println("子线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }, "thread-0");

    //设置线程为后台线程
    thread.setDaemon(true);
    thread.start();

    System.out.println("线程的ID(JVM):"+thread.getId());
    System.out.println("线程的名字:"+thread.getName());

    //TimeUnit.SECONDS.sleep(1);
    System.out.println("线程的状态:"+thread.getState());
    System.out.println("线程的优先级:"+thread.getPriority());

    System.out.println("线程是否为后台线程:"+thread.isDaemon());

    //thread.interrupt();
    System.out.println("线程是否被打断:"+thread.isInterrupted());


    System.out.println("线程是否存活:"+thread.isAlive());
    thread.join();
    System.out.println("线程是否存活:"+thread.isAlive());
}

什么是后台线程?

前台线程 后台线程
作用 通常用于执行与客户端的交互任务, 前台线程可以影响用户体验,因此它们的响应速度较高 用于执行不需要用户直接参与或感知的任务, 通常用于处理一些较为耗时的操作,如数据加载、文件处理等
是否阻塞 通常用于执行与客户端的交互任务, 前台线程可以影响用户体验,因此它们的响应速度较高 用于执行不需要用户直接参与或感知的任务, 通常用于处理一些较为耗时的操作,如数据加载、文件处理等
生命周期 启动和结束通常与应用程序的启动和结束相一致 生命周期通常独立于应用程序的生命周期

线程优先级有什么用?
用于告诉操作系统调度器在多个线程可运行时, 应该选择哪个先执行

具有较高优先级的线程在竞争CPU时更有可能被调度执行。高优先级的线程更有可能更早地得到执行,因此可以更快地响应某些事件。这对于实时系统和需要低延迟响应的应用程序非常重要

什么情况下会使用interrupt方法

  • 优雅的终止线程
    通过调用 interrupt() 方法,可以通知目标线程应该中断并终止执行。线程在检测到中断状态后可以采取适当的措施,例如释放资源、完成未完成的任务等。
  • 中断阻塞操作
    如果线程在执行阻塞操作(如 sleep()、wait()、join() 等)时,可以通过调用 interrupt() 来中断线程,以提前结束阻塞状态,使线程能够响应中断并采取相应措施。
  • 线程间通信
    interrupt() 方法可以用作线程间的一种通信机制。当一个线程希望通知其他线程进行某种操作时,可以通过中断其他线程来引起其注意。
  • 检查中断状态
    线程可以通过调用 isInterrupted() 或 Thread.interrupted() 来检查自己是否被中断,并相应地进行处理。这可以帮助线程在合适的时候做出决策,例如提前结束执行或执行一些清理工作。
  • 线程池关联
    在使用线程池时,interrupt() 方法可以用于中断某个任务的执行。线程池可以通过 shutdownNow() 方法中断所有线程池中的任务,这将调用每个任务的 interrupt() 方法。

interrupt()和stop()方法的区别
interrupt和stop方法都是用来中断线程的方法, 但它们在实现上有很大的不同, 在java中建议使用interrupt()而不是stop, 两者的区别如下

interrupt() stop()
作用 Thread类的实例方法, 用于中断目标线程的执行 Thread类的实例方法, 用于立即停止目标线程的执行
调用 将线程的中断状态设置为true 会导致线程立即终止,不会有机会去执行清理工作或完成未完成的任务

interrupt()是否会中断目标线程的执行

  • 如果线程处于阻塞状态(如 sleep()、wait()、join() 等)
    它会抛出InterruptedException, 并清除中断状态
  • 如果线程处于非阻塞状态
    仅仅只是将线程的中断状态设置为true

interrupt() 方法主要是一种协作机制,通知线程应该中断并终止执行

Java:关于Java中的线程中断的几种方法

join()方法的作用
作用是让一个线程等待另一个线程执行结束

如果线程A中调用了线程B的join()方法, 那么线程A将会等待线程B执行完成, 然后再继续执行

join方法有以下几个特点:

  1. 等待: 调用join的线程将阻塞, 直到被调用的线程执行完成
  2. 顺序执行: 如果在主线程中调用多个线程的join方法, 那么这些线程将按照调用join的顺序依次执行
public class ThreadTest {

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new MyRunnable(), "Thread-1");
        Thread thread2 = new Thread(new MyRunnable(), "Thread-2");

        // 启动线程1
        thread1.start();
        thread1.join();

        // 启动线程2
        thread2.start();

    }

}

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try {
                Thread.sleep(100); // 模拟一些耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

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