一、方法
- start():启动线程。调用start()方法后,线程会被纳入到线程调度器中,等待调度执行。
- run():线程的执行逻辑。需要重写该方法,在其中编写线程需要执行的任务逻辑。
- join():等待线程终止。调用该方法会使当前线程等待被调用的线程执行完毕再继续进行。
- sleep(long millis):线程休眠。使当前线程暂停执行指定的时间(以毫秒为单位),让出CPU资源。
- yield():线程让步。暂停当前正在执行的线程,让其他具有相同优先级的线程有机会执行。
- interrupt():中断线程。向目标线程发送中断信号,该信号可以由目标线程的代码检测到并进行相应的处理。
- isInterrupted():检查线程是否被中断。判断当前线程是否处于中断状态。
- setPriority(int priority):设置线程优先级。用于设置线程的执行优先级,指定取值范围为1-10(10为最高优先级)。
- getState():获取线程状态。返回当前线程的状态,如NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED等。
- wait()、notify()和notifyAll():用于线程之间的通信。wait()使当前线程进入等待状态,notify()唤醒一个等待中的线程,notifyAll()唤醒所有等待中的线程。
- synchronized关键字:线程同步。使用synchronized关键字修饰方法或代码块,实现对共享资源的互斥访问,防止多个线程同时修改数据。
二、线程的优先级:
线程的优先级是指在多线程环境下,操作系统对线程调度的一种依据,用于确定线程获得 CPU 执行时间的相对权重。Java中线程的优先级范围是1-10,其中1表示最低优先级,10表示最高优先级。
线程的优先级可以通过setPriority(int priority)方法进行设置,也可以使用getPriority()方法获取当前线程的优先级。
需要注意的是,线程优先级只是给调度器一个提示,无法确保高优先级的线程一定会先执行。操作系统的调度算法会根据不同的实现和策略进行线程调度,因此不能过度依赖线程的优先级来编写可靠的程序。
优先级较高的线程在竞争CPU资源时可能会更容易被调度执行,但并不保证一定会获得更多的执行时间。实际上,线程的调度顺序是由操作系统决定的,并且受到多个因素的影响,如线程的优先级、线程的状态、调度算法等。
public class ThreadPriorityExample {
public static void main(String[] args) {
Thread thread1 = new SampleThread();
Thread thread2 = new SampleThread();
// 设置线程1的优先级为最低
thread1.setPriority(Thread.MIN_PRIORITY);
// 设置线程2的优先级为最高
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
}
class SampleThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("当前执行的线程: " + Thread.currentThread().getName()
+ ",优先级: " + Thread.currentThread().getPriority()
+ ",运行次数: " + i);
}
}
}
在这个示例中,我们创建了两个线程thread1和thread2,它们都是SampleThread类的实例。然后,我们使用setPriority()方法将thread1的优先级设置为最低(1),将thread2的优先级设置为最高(10)。
在SampleThread中的run()方法中,线程会打印当前执行的线程名称、优先级以及运行次数。我们通过运行这段代码,可以观察到线程1的运行次数较少,而线程2的运行次数较多。这是因为线程2具有更高的优先级,相对而言更容易被调度执行。
三、守护线程:
守护线程(Daemon Thread)是在后台提供一种支持服务的线程,主要用于为其他非守护线程提供支持。
与普通线程不同,当所有非守护线程结束时,守护线程会被自动终止,无需显式地调用stop()或interrupt()方法来终止它们。
在Java中,可以通过setDaemon(true)方法将线程设置为守护线程。默认情况下,创建的线程都是非守护线程。将线程设置为守护线程必须在启动线程之前进行,否则会抛出IllegalThreadStateException异常。
public class DaemonThreadExample {
public static void main(String[] args) {
Thread thread = new SampleThread();
// 设置线程为守护线程
thread.setDaemon(true);
thread.start();
// 主线程休眠一秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束");
}
}
class SampleThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("守护线程正在运行");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在这个示例中,我们创建了一个守护线程SampleThread。在main()方法中,我们将thread设置为守护线程,并启动它。主线程休眠一秒钟后输出"主线程结束",然后程序结束。
由于SampleThread是守护线程,它会在所有非守护线程(这里只有主线程)结束时自动终止。因此,当主线程结束后,守护线程也会随之结束。
需要注意的是,守护线程不能用于执行需要完整执行的任务,因为它们的终止时间无法确定。守护线程通常用于提供支持性质的服务,比如垃圾回收器(GC)线程。
四、礼让线程:
在多线程编程中,礼让线程是一种线程间的合作机制,它允许一个线程主动放弃当前的CPU执行权,让其他线程有机会执行。通过礼让线程,我们可以实现线程间的优先级调度或协调执行。
在Java中,可以使用Thread.yield()方法来实现线程的礼让。调用yield()方法的线程会暂停当前的执行,让出CPU资源给其他具有相同优先级的线程。然后,线程调度器会从等待队列中选择一个线程进行执行。
public class ThreadYieldExample {
public static void main(String[] args) {
Thread thread1 = new SampleThread("Thread 1");
Thread thread2 = new SampleThread("Thread 2");
thread1.start();
thread2.start();
}
}
class SampleThread extends Thread {
public SampleThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(getName() + ": " + i);
// 当 i 等于 3 的时候,调用 yield() 方法礼让CPU资源
if (i == 3) {
Thread.yield();
}
}
}
}
在这个示例中,我们创建了两个线程thread1和thread2,它们都是SampleThread类的实例。在SampleThread的run()方法中,线程会打印名称和计数器变量的值。当计数器变量i等于3时,调用Thread.yield()方法礼让CPU资源。
通过运行这段代码,我们可以观察到以下结果:
Thread 1: 1
Thread 2: 1
Thread 1: 2
Thread 2: 2
Thread 1: 3
Thread 2: 3
Thread 2: 4
Thread 2: 5
Thread 1: 4
Thread 1: 5
在这个示例中,当i等于3时,thread1主动礼让了CPU资源,此时thread2得到执行的机会。然后,thread2继续执行直到完成。最后,thread1再次获得CPU执行权并完成其剩余的计数。
需要注意的是,礼让线程的效果取决于操作系统和线程调度器的实现。在某些情况下,即使调用了yield()方法,当前线程仍然可能继续执行,而不是礼让给其他线程。因此,不能完全依赖yield()方法来实现线程调度。
五、插入线程:
当一个线程调用另一个线程的join()方法时,它会暂停自己的执行,并等待被调用线程执行完成后再继续执行。
join()方法有两个重载版本:
- join():该方法会使当前线程等待被调用线程执行完成。
- join(long millis):该方法会使当前线程等待被调用线程执行完成,但最多等待指定的时间(以毫秒为单位)。如果在指定时间内被调用线程没有执行完成,当前线程会继续执行。
public class JoinExample {
public static void main(String[] args) {
System.out.println("主线程开始执行");
// 创建并启动线程A
Thread threadA = new Thread(new TaskA());
threadA.start();
try {
// 等待线程A执行完成
threadA.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程继续执行");
}
}
class TaskA implements Runnable {
@Override
public void run() {
System.out.println("线程A开始执行");
try {
// 模拟线程A的工作
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程A执行完成");
}
}
主线程开始执行
线程A开始执行
线程A执行完成
主线程继续执行
从输出结果可以看出,在主线程中调用threadA.join()方法后,主线程进入等待状态,直到线程A执行完成,主线程才会继续执行。
join()方法常常用于需要等待其他线程完成工作后再进行后续操作的场景。例如,主线程需要等待所有子线程完成后再进行结果的汇总或后续计算等。