1.4 线程的状态和基本操作

1 线程的生命周期及五种基本状态?

1 新建(new):当线程对象被创建时,它处于新建状态。

2 就绪(runnable):线程对象创建后,当调用线程对象的 start()方法,该线程处于就绪状态,等待被线程调度选中,获取cpu的使用权。

3 运行(running):就绪状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码,则进入运行态。注:就绪状态是进入到运行状态的唯一入口,线程要想进入运行状态执行,首先必须处于就绪状态中;

4 阻塞(block):处于运行状态中的线程需要等待某些事件的发生,比如等待 I/O 操作完成或等待某个锁的释放等,暂时放弃对 CPU的使用权。
此时进入阻塞状态,当等待的事件发生后,线程会被唤醒,进入就绪状态,才有机会再次被 CPU 调用以进入到运行状态。

5 终止(Terminated):当线程完成任务后,或者因异常退出了run()方法,它会进入终止状态。此时,系统会释放线程所占用的资源,并销毁线程的数据结构。
在这里插入图片描述

2 阻塞的情况分三种:

1 等待阻塞:运行状态中的线程执行 wait()方法,JVM会把该线程放入等待队列(waitting queue)中,使本线程进入到等待阻塞状态;
2 同步阻塞:线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),则JVM会把该线程放入锁池(lock pool)中,线程会进入同步阻塞状态;
3 其他阻塞: 通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
当一个线程a调用另一个线程b的 join() 方法时,该线程a会被阻塞,直到被调用的线程b执行完毕后,该线程a再继续执行。

3 Java 中用到的线程调度算法是什么?

线程调度算法是:为多个线程分配 CPU 使用权的特定机制。
常见的调度模型有两种:分时调度模型和抢占式调度模型。
在 Java 中,默认采用的是抢占式调度模型,线程运行时可以被操作系统强制中断,然后将 CPU 时间片分配给更高优先级的线程。

1 分时调度模型(Time Sharing Model) 是指将 CPU 时间分成若干个时间片,每个时间片分配给不同的进程或线程使用,使得每个进程或线程都能够得到一定的执行时间,从而实现多个进程或线程的并发执行。

2 抢占式调度模型(Preemptive Model) 是指操作系统可以强制中断正在执行的进程或线程,将 CPU 时间片分配给更高优先级的进程或线程。当更高优先级的进程或线程出现时,操作系统会立即暂停正在执行的进程或线程,将 CPU 时间片分配给更高优先级的进程或线程执行,从而保证了系统中高优先级进程或线程的及时响应能力。

4 请说出与线程同步以及线程调度相关的方法

1 wait() 方法: 使线程进入等待状态,等待其他线程通知,且释放对象锁,该方法必须在同步代码块中调用。

2 yield() 方法: 使当前线程放弃 CPU 资源,让其他线程执行,但不会释放锁。

3 sleep() 方法: 使当前线程进入睡眠状态,等待一定时间后重新唤醒,但不会释放锁。

4 join() 方法: 使当前线程等待另一个线程执行完毕后再继续执行。

5 notify() 方法: 用于唤醒一个处于等待状态的线程,该方法必须在同步代码块中调用。

6 notifyAll() 方法: 用于唤醒所有处于等待状态的线程,该方法必须在同步代码块中调用。

5 sleep() 和 wait() 有什么区别?

在 Java 中,sleep() 和 wait() 都是用来暂停线程执行的方法,但是它们有以下几个区别:

1 所属类不同: sleep() 方法是 Thread 类的静态方法,可以在任何地方使用;而 wait() 方法是 Object 类的方法,只能在同步代码块中使用。

2 用法不同: sleep() 方法可以指定暂停的时间,线程会暂停指定的时间后自动恢复执行;而 wait() 方法需要等待其他线程调用 notify() 或 notifyAll() 方法来通知等待的线程才能恢复执行。

3 是否释放锁 sleep() 方法不会释放锁;而 wait() 方法会释放锁,其他线程可以获取锁并执行同步代码块。

因此,sleep() 方法主要用于控制线程的执行速度,可以使线程暂停一段时间后继续执行;而 wait() 方法主要用于线程之间的协作,可以让一个线程等待其他线程的通知,或者让其他线程等待当前线程执行完毕后再继续执行。

6 你是如何调用 wait() 方法的?使用 if 块还是循环?为什么?

在调用 wait() 方法时,一般要使用 while 循环来检查条件,而不是使用 if 块。

例如,假设有一个线程a在等待某个条件变为真时调用了 wait() 方法,当条件变为真时,另一个线程b调用了 notify() 方法来唤醒等待的线程a。此时,如果使用 if 块来检查条件,等待的线程a可能会继续执行下去,因为此时条件已经变为真了。但是,如果在等待期间有其他线程c修改了条件,并且该条件再次变为假,那么等待的线程a就会出现问题。

因此,正确的方式是使用 while 循环来检查条件,当等待的线程a被唤醒后,它需要再次检查条件是否满足,如果条件不满足,就继续等待。这样可以避免线程因为条件变化而出现错误。

synchronized (lock) {
    while (!condition) {
        try {
            lock.wait();
        } catch (InterruptedException e) {
            // 处理异常
        }
    }
    // 执行需要同步的操作
}

在这个示例中,使用了 while 循环来检查条件是否满足,当条件不满足时,线程会继续等待。当条件满足时,线程会向下执行。

如果使用 if 块来检查条件,代码可能会出现如下问题:

synchronized (lock) {
    if (!condition) {
        try {
            lock.wait();
        } catch (InterruptedException e) {
            // 处理异常
        }
    }
    // 执行需要同步的操作
}

在这个示例中,如果线程被唤醒后,条件已经不满足了,但是线程还是会执行同步操作,这可能会导致程序出现错误。

因此,建议使用 while 循环来检查条件,以确保线程在唤醒后再次检查条件是否满足。

7 为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?

首先本质上 wait 和 notify 是用于线程之间通信的,wait() 表示当前获取到锁的线程进入休眠状态并释放锁,notify() 表示当前获取到锁的线程唤醒曾经获取到该锁的 wait() 的休眠线程,这里有一个很关键的点,就是他们通信的基础获得或者曾经获得的是同一个锁

而在 Java 中,锁是放在对象上的,所以可以说这两个线程的通信依赖于这个对象才行,如果不放在对象上,也可以用 Thread.wait( Object o) 把对象传递进去,但是从设计的角度来说,还不如把方法直接放在对象上。

又因为Java中的所有类都继承自Object类,因此将wait()、notify()和notifyAll()方法定义在Object类中可以保证所有的类都有这些方法。

如果将这些方法定义在Thread类中,并不是所有的类都有这些方法,会造成线程间的通信和协调方法不统一。

总之,wait()、notify()和notifyAll()方法的定义是为了方便Java中对象之间的通信和协调,而Object类是所有Java类的基类,因此在这个层次定义这些方法是最为合理和方便的。

8 为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?

当一个线程需要调用一个对象的wait或者notify方法时,首先需要获得该对象的同步锁,而只有进入该对象的同步块中才能获得对象的锁,所以这些方法必须在同步块中被调用。

9 Thread 类中的 yield 方法有什么作用?

yield() 方法用于暂停当前线程的执行,并将 CPU 的使用权让给同一优先级的其他线程。
如果没有其他线程需要执行,那么原来的线程将立即恢复执行。如果有其他线程需要执行,那么当前线程将进入就绪状态,等待调度程序再次分配 CPU 时间。

10 为什么 Thread 类的 sleep()和 yield ()方法是静态的?

因为这些方法不需要访问或修改特定线程对象的状态,而是直接操作当前线程的状态。因此,将它们定义为静态方法可以方便地在任何线程中使用。

11 线程的 sleep()方法和 yield()方法有什么区别?

(1) sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;

(2) 线程执行 sleep()方法后转入阻塞(blocked)状态,而执行 yield()方法后转入就绪(ready)状态;
(3)sleep()方法声明抛出 InterruptedException,而 yield()方法没有声明任何异常;

12 如何停止一个正在运行的线程?

1 使用 volatile 标志位停止线程:在线程中使用一个标志位来标识线程是否需要停止,如果标志位被设置为 true,则线程会自行停止执行。

public class ThreadTest extends Thread {

	    public volatile boolean exit = false; 

	    public void run() { 
	        while (!exit); 
	    } 
	public static void main(String[] args) throws InterruptedException {
		ThreadTest thread = new ThreadTest(); 
        thread.start(); 
        Thread.sleep(5000); // 主线程延迟5秒 
        thread.exit = true;  // 终止线程thread 
        thread.join(); 
        System.out.println("线程退出!"); 
	}

}

2 使用stop方法强行终止,但是不推荐这个方法,因为stop是过期方法。

public class ThreadTest{

	 static class Thread1 extends Thread {
	        @Override
	        public void run() {
	            for (int i = 0; i < 500000; i++) {
	                System.out.println("打印的数字"+i);
	            }
	        }
	 }
	public static void main(String[] args) throws InterruptedException {
		Thread1 thread1 = new Thread1();
		thread1.start();
        //保证子线程进入运行状态,避免还没运行就被终止
        Thread.sleep(100);
        //暴力停止子线程
        thread1.stop();
	}

}

3 使用 interrupt() 方法停止线程:通过调用线程的 interrupt() 方法来请求线程停止执行。实际上只是给线程设置一个中断标志,但是线程依旧会执行。

public class ThreadTest extends Thread {

	   private boolean flag = true;

	    @Override
	    public void run() {
	        while (flag) {
	            synchronized (this) {

	                try {
	                    sleep(10000);
	                } catch (InterruptedException e) {
	                    e.printStackTrace();
	                    this.stopThread();
	                }
	            }
	        }
	    }

	    public void stopThread() {
	        System.out.println("线程已经退出。。。");
	        this.flag = false;
	    }
	public static void main(String[] args) throws InterruptedException {
		    ThreadTest threadTest = new ThreadTest();
		    threadTest.start();
	        System.out.println("线程开始");
	        try {
	            Thread.sleep(5000);
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        }
	        threadTest.interrupt();
	}

}

13 Java 中 interrupt、interrupted 和 isInterrupted 方法的区别?

链接: https://zhuanlan.zhihu.com/p/48566626
1 interrupt:用于中断线程。调用该方法的线程的状态为将被置为”中断”状态。
注意:线程中断仅仅是置线程的中断状态位,不会停止线程。
需要用户自己监视线程的状态为并做处理。

2 调用 interrupted() 方法会清除当前线程的中断状态。
而调用 isInterrupted() 方法不会清除中断状态。

3 在一个线程上调用 interrupted() 方法时,如果线程被中断了,则第一次调用返回 true,而后续调用返回 false。
而在 isInterrupted() 方法中,只要线程被中断了,每次调用都返回 true。

14 什么是阻塞式方法?

阻塞式方法
怎么理解阻塞和非阻塞,同步和非同步
在阻塞式方法执行过程中,调用该方法的线程会被阻塞(block),直到方法执行完成并返回结果,期间不会执行其他任务。

例如,在Java中,ServerSocket的accept()方法会一直等待客户端连接,直到有客户端连接才会返回。

15 Java 中你怎样唤醒一个阻塞的线程?

线程的阻塞与唤醒
Java 并发编程:多线程如何实现阻塞与唤醒

16 如何在两个线程间共享数据?

多个线程间共享数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值