Java基础夯实【进阶】——八股文【2024面试题案例代码】

1、Java当中什么是线程和进程

在Java中,线程和进程是两个非常重要的概念。进程可以被视为一个执行中的程序的实例,它拥有自己的内存空间和系统资源。而线程则是进程中的一个实体,由进程创建,并允许程序在同一时刻执行多个任务。Java提供了两种实现多线程的方式:一种是通过继承Thread类并重写run()方法来创建线程;另一种是实现Runnable接口,将逻辑代码写入该接口的实现类后,将这个实现类的实例作为参数传递给Thread类的构造函数,从而创建线程。

以下是一个具体的Java多线程案例代码:

// 任务类
public class MyRunnable implements Runnable {@Overridepublic void run () {for (int i = 1; i <= 5; i++) {System.out.println("MyRunnable==》" + i);}}
}public class Main {public static void main(String[] args) {// 创建线程对象MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);// 启动线程thread.start();}
}

2、Java创建线程有哪几种方式

Java创建线程有三种方式:

  1. 继承Thread类,重写run()方法。
  2. 实现Runnable接口,将逻辑代码写入该接口的实现类后,将这个实现类的实例作为参数传递给Thread类的构造函数,从而创建线程。
  3. 实现Callable接口,重写call()方法

以下是两种创建线程的案例代码:

  1. 继承Thread类,重写run()方法:
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("MyThread==》" + i);}}
}public class Main {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start(); // 启动线程}
}
  1. 实现Runnable接口,将逻辑代码写入该接口的实现类后,将这个实现类的实例作为参数传递给Thread类的构造函数,从而创建线程:
public class MyRunnable implements Runnable {@Overridepublic void run () {for (int i = 0; i < 5; i++) {System.out.println("MyRunnable==》" + i);}}
}public class Main {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable); // 创建线程对象thread.start(); // 启动线程}
}
  1. 实现Callable接口,重写call()方法
    Callable:有返回值的线程,能取消线程,可以判断线程是否执行完毕
public class Main {public static void main(String[] args) throws Exception {// 将Callable包装成FutureTask,FutureTask也是一种RunnableMyCallable callable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(callable);new Thread(futureTask).start();// get方法会阻塞调用的线程Integer sum = futureTask.get();System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);}
}class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");int sum = 0;for (int i = 0; i <= 100000; i++) {sum += i;}Thread.sleep(5000);System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");return sum;}
}

Callable 也是一种函数式接口

@FunctionalInterface
public interface Callable<V> {V call() throws Exception;
}

FutureTask

public class FutureTask<V> implements RunnableFuture<V> {// 构造函数public FutureTask(Callable<V> callable);// 取消线程public boolean cancel(boolean mayInterruptIfRunning);// 判断线程public boolean isDone();// 获取线程执行结果public V get() throws InterruptedException, ExecutionException;
}

RunnableFuture

public interface RunnableFuture<V> extends Runnable, Future<V> {void run();
}

三种方式比较:

  • Thread: 继承方式, 不建议使用, 因为Java是单继承的,继承了Thread就没办法继承其它类了,不够灵活
  • Runnable: 实现接口,比Thread类更加灵活,没有单继承的限制
  • Callable: Thread和Runnable都是重写的run()方法并且没有返回值,Callable是重写的call()方法并且有返回值并可以借助FutureTask类来判断线程是否已经执行完毕或者取消线程执行
    当线程不需要返回值时使用Runnable,需要返回值时就使用Callable,一般情况下不直接把线程体代码放到Thread类中,一般通过Thread类来启动线程
  • Thread类是实现Runnable,Callable封装成FutureTask,FutureTask实现RunnableFuture,RunnableFuture继承Runnable,所以Callable也算是一种Runnable,所以三种实现方式本质上都是Runnable实现

3、Java线程状态

Java线程状态有以下6种:
在这里插入图片描述

  1. 新建(New):线程对象被创建后,就进入了新建状态。
  2. 就绪(Runnable):当调用线程的start()方法时,线程进入就绪状态。此时线程已经获取了除CPU资源外的所有资源,只等待CPU资源的分配。
  3. 运行(Running):当就绪状态的线程获得CPU资源时,线程进入运行状态。此时线程开始执行run()方法中的代码。
  4. 阻塞(Blocked):线程在运行过程中,可能会因为某些原因而进入阻塞状态。例如,线程调用了sleep()、wait()等方法,或者试图获取一个正在被其他线程持有的锁。
  5. 等待(Waiting):当线程处于阻塞状态时,如果其他线程调用了该线程的notify()或notifyAll()方法,那么该线程将进入等待状态。等待状态下的线程不会占用CPU资源,直到其他线程再次唤醒它。
  6. 超时等待(Timed Waiting):与等待状态类似,但超时等待状态下的线程会在指定的超时时间内自动返回到等待队列中。

以下是一个简单的Java多线程案例代码:

public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("MyThread==》" + i);}}
}public class Main {public static void main(String[] args) {MyThread myThread = new MyThread(); // 新建状态myThread.start(); // 就绪状态myThread.run(); // 运行状态}
}

4、Java线程并行与并发

Java线程的并行和并发是指在一个程序中同时执行多个线程,以提高程序的效率。

并行是指多个线程同时执行,每个线程都有自己的独立的执行路径,互不干扰。

例如,在计算某个问题时,可以将该问题分成若干个子问题,然后创建多个线程分别处理这些子问题,最后将各个子问题的计算结果合并得到最终结果。

并发是指多个线程交替执行,每个线程都会在某些时刻占有CPU资源进行计算。

例如,在处理大量数据时,可以创建一个生产者线程和一个消费者线程,生产者线程负责生成数据,消费者线程负责处理数据,两个线程交替执行,从而提高程序的效率。

1、以下是一个Java多线程并行案例代码:

class MyRunnable implements Runnable {private String name;public MyRunnable(String name) {this.name = name;}@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(name + "正在执行任务:" + i);}}
}public class MultiThreadDemo {public static void main(String[] args) {Thread thread1 = new Thread(new MyRunnable("线程1"));Thread thread2 = new Thread(new MyRunnable("线程2"));Thread thread3 = new Thread(new MyRunnable("线程3"));thread1.start();thread2.start();thread3.start();}
}

在这个例子中,我们创建了一个实现Runnable接口的类MyRunnable,它有一个run方法,用于执行任务。在主函数中,我们创建了三个线程对象,分别传入不同的名称,并将它们启动。这样,这三个线程将并行执行任务。

2、以下是一个Java多线程并发案例代码:

public class MultiThreadDemo {public static void main(String[] args) {// 创建两个线程对象Thread thread1 = new Thread(new MyRunnable(), "线程1");Thread thread2 = new Thread(new MyRunnable(), "线程2");// 启动线程thread1.start();thread2.start();}
}class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "正在执行任务:" + i);try {// 模拟线程执行任务需要的时间Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

在这个例子中,我们创建了一个名为MultiThreadDemo的类,其中包含一个main方法。在main方法中,我们创建了两个线程对象thread1thread2,并将它们分别命名为"线程1"和"线程2"。然后,我们调用start()方法启动这两个线程。

我们还创建了一个实现Runnable接口的类MyRunnable,并在其中定义了run方法。在run方法中,我们使用一个循环来模拟线程执行任务的过程。在每次循环中,我们打印出当前线程的名称和任务编号,并让线程休眠1秒钟以模拟任务执行所需的时间。

5、什么是同步执行和异步执行

同步执行和异步执行是Java线程编程中的两种执行方式。

  1. 同步执行:当一个线程在执行某个方法时,其他线程必须等待该线程执行完毕后才能继续执行。这种执行方式称为同步执行。

  2. 异步执行:当一个线程在执行某个方法时,其他线程可以同时执行其他任务,而不需要等待该线程执行完毕。这种执行方式称为异步执行。

下面分别给出同步执行和异步执行的示例代码:

同步执行示例代码:

public class SynchronizedExample {public static void main(String[] args) {Object lock = new Object();Thread thread1 = new Thread(() -> {synchronized (lock) {System.out.println("线程1开始执行");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1执行完毕");}});Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println("线程2开始执行");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2执行完毕");}});thread1.start();thread2.start();}
}

输出结果

线程1开始执行
线程1执行完毕
线程2开始执行
线程2执行完毕

异步执行示例代码:

public class AsynchronousExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("线程1开始执行");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1执行完毕");});Thread thread2 = new Thread(() -> {System.out.println("线程2开始执行");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2执行完毕");});thread1.start();thread2.start();}
}

输出结果

线程1开始执行
线程2开始执行
线程2执行完毕
线程1执行完毕

输出结果

线程1开始执行
线程2开始执行
线程1执行完毕
线程2执行完毕

6、Java多线程操作数据库

在Java中,多线程操作数据库可以通过以下几种方式实现:

  1. 使用ExecutorServiceCallable接口:

ExecutorService是Java中的一个接口,它属于java.util.concurrent包。这个接口表述了异步执行的机制,并且可以让任务在后台执行。ExecutorService实例就像一个线程池,它是线程池的一个定义,并且在这个接口中定义了和后台任务执行相关的方法。

ExecutorService的使用可以带来很多优点。首先,由于请求到达时,线程已经存在,所以响应延迟低。其次,多个任务复用线程,避免了线程的重复创建和销毁,这有助于提高系统的性能。此外,通过使用ExecutorService,我们可以方便地创建多线程执行环境。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;public class MultiThreadedDatabaseOperation {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(5);List<Future<Void>> futures = new ArrayList<>();for (int i = 0; i < 10; i++) {Callable<Void> task = () -> {try {Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username", "password");Statement statement = connection.createStatement();statement.executeUpdate("INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2')");connection.close();} catch (SQLException e) {e.printStackTrace();}return null;};futures.add(executor.submit(task));}for (Future<Void> future : futures) {try {future.get();} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}executor.shutdown();}
}
  1. 使用ThreadPoolExecutorRunnable接口:

ThreadPoolExecutor是Java中的一个线程池实现类,它属于java.util.concurrent包。
这个类是线程池中非常关键的一部分,主要用于管理和控制线程的创建与销毁。

使用ThreadPoolExecutor的主要优点在于,它可以避免频繁地创建和销毁线程所带来的开销。当系统中频繁地创建线程时,如果线程过多,会带来调度开销,进而影响缓存局部性和整体性能。

而ThreadPoolExecutor维护着多个线程,等待着监督管理者分配可并发执行的任务。这种方式不仅能够保证内核的充分利用,还能防止过分调度。

通过ThreadPoolExecutor的execute()方法,我们可以执行Runnable任务;

另外,通过ThreadPoolExecutor的submit()方法,我们不仅可以执行Runnable任务,还可以执行Callable任务,并且能够获取异步的执行结果。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.*;public class MultiThreadedDatabaseOperation {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));for (int i = 0; i < 10; i++) {executor.execute(() -> {try {Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username", "password");Statement statement = connection.createStatement();statement.executeUpdate("INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2')");connection.close();} catch (SQLException e) {e.printStackTrace();}});}executor.shutdown();}
}

这两种方法都可以实现多线程操作数据库,但第一种方法使用了ExecutorServiceCallable接口,而第二种方法使用了ThreadPoolExecutorRunnable接口。

你可以根据实际需求选择合适的方法。

7、Java线程池概念和用法

Java线程池是一种管理线程的工具,它可以有效地控制并发线程的数量,避免过多的线程导致系统资源耗尽。

线程池的主要作用是复用已经创建的线程,减少线程创建和销毁的开销。

Java中创建线程池的方式有以下几种:

  1. 使用Executors工厂方法创建固定大小的线程池:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {// 创建一个固定大小为5的线程池ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {executorService.submit(new Runnable() {@Overridepublic void run() {System.out.println("线程名:" + Thread.currentThread().getName());}});}// 关闭线程池executorService.shutdown();}
}
  1. 使用ThreadPoolExecutor类创建自定义大小的线程池:
import java.util.concurrent.*;public class CustomThreadPoolDemo {public static void main(String[] args) {// 创建一个自定义大小的线程池ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));for (int i = 0; i < 10; i++) {threadPoolExecutor.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程名:" + Thread.currentThread().getName());}});}// 关闭线程池threadPoolExecutor.shutdown();}
}

new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10)); 是一个创建线程池的代码片段,其中各个参数的含义如下:

  • 2:表示线程池的核心线程数(即最小线程数)。当任务数量小于核心线程数时,线程池会创建新的线程来执行任务。
  • 5:表示线程池的最大线程数(即最大线程数)。当任务数量大于核心线程数且小于最大线程数时,线程池会创建新的线程来执行任务。
  • 60:表示线程空闲时间,超过这个时间的线程会被终止。单位是秒。
  • TimeUnit.SECONDS:表示时间单位是秒。
  • new LinkedBlockingQueue<>(10):表示线程池使用一个容量为10的阻塞队列来存储待执行的任务。当队列满时,新提交的任务会被阻塞等待队列中有空闲位置。

综上所述,这段代码创建了一个具有2个核心线程、5个最大线程、线程空闲时间为60秒、使用LinkedBlockingQueue作为任务队列的线程池。
3. 使用ExecutorService接口的实现类创建线程池:

import java.util.concurrent.*;public class InterfaceThreadPoolDemo {public static void main(String[] args) {// 创建一个固定大小为5的线程池ExecutorService executorService = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));for (int i = 0; i < 10; i++) {executorService.execute(new Runnable() {@Overridepublic void run() {System.out.println("线程名:" + Thread.currentThread().getName());}});}// 关闭线程池executorService.shutdown();}
}

参数的含义如下:

  1. 2:这是核心线程数。线程池中的线程数量至少为这个值。如果任务队列满,那么即使有空闲的线程,也不会创建新的线程来执行任务。

  2. 5:这是最大线程数。线程池中的线程数量最多为这个值。如果任务队列满,并且当前线程数已经达到最大线程数,那么新提交的任务会被阻塞,直到有线程完成任务并释放资源。

  3. 60:这是线程空闲时间。当线程池中的线程空闲超过这个时间后,这些线程会被终止。

  4. TimeUnit.SECONDS:这是时间单位。在这个例子中,空闲时间是以秒为单位的。

  5. new LinkedBlockingQueue<>(10):这是任务队列。这是一个阻塞队列,用于存储待执行的任务。当队列满时,新提交的任务会被阻塞等待队列中有空闲位置。

所以,这段代码创建了一个线程池,其核心线程数为2,最大线程数为5,线程空闲时间为60秒,使用LinkedBlockingQueue作为任务队列,队列的最大容量为10。

8、Java线程什么是Callable和Future

Callable和Future是Java中用于处理并发编程的两个接口。

  1. Callable:它是一个带有返回值的任务,可以抛出异常。实现Callable接口的类需要重写call()方法,该方法包含任务的具体逻辑。当调用call()方法时,会返回一个Future对象,表示任务的结果。

  2. Future:它是一个表示异步计算结果的接口。通过Future对象,可以在计算完成后获取计算结果,或者取消计算。

下面是一个简单的案例代码:

import java.util.concurrent.*;public class CallableAndFutureExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);Callable<Integer> callableTask = () -> {int sum = 0;for (int i = 0; i < 10; i++) {sum += i;}return sum;};Future<Integer> future = executorService.submit(callableTask);try {Integer result = future.get(); // 获取计算结果,如果计算尚未完成,此方法会阻塞等待System.out.println("计算结果:" + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();} finally {executorService.shutdown(); // 关闭线程池}}
}

在这个例子中,我们创建了一个Callable任务,该任务计算0到9的和。然后,我们将这个任务提交给线程池执行,并通过Future对象获取计算结果。

9、Java线程常用方法

Java线程常用方法有以下几种:

  1. start():启动线程。
  2. run():线程执行的方法,需要在子类中重写。
  3. join():等待线程执行完毕。
  4. sleep():让当前线程暂停指定的时间。
  5. yield():让当前线程放弃CPU控制权,让其他线程有机会执行。
  6. interrupt():中断线程。
  7. isAlive():判断线程是否存活。
  8. getName():获取线程名称。
  9. setName():设置线程名称。

以下是一个简单的Java线程示例代码:

public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "正在运行,i = " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start(); // 启动线程try {myThread.join(); // 等待线程执行完毕} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主线程结束");}
}

10、什么是线程的上下文切换

线程的上下文切换是指在多线程环境下,CPU从一个线程切换到另一个线程执行的过程。这个过程涉及到保存当前线程的状态(如寄存器值、栈指针等),并加载另一个线程的状态以继续执行。上下文切换会消耗一定的系统资源,因此需要尽量减少不必要的上下文切换。

案例代码:

public class ContextSwitchExample {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("线程1:" + i);}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("线程2:" + i);}}});t1.start();t2.start();}
}

在这个例子中,我们创建了两个线程t1和t2,分别打印0到999的数字。当这两个线程交替执行时,就发生了上下文切换。

11、sleep和wait的区别?

Java中的sleep和wait方法都用于线程的暂停执行,但它们之间有一些区别:

  1. sleep方法是Thread类的静态方法,它可以让当前线程暂停指定的毫秒数。
    在休眠期间,线程不会释放锁资源。因此,其他线程无法进入同步代码块或方法。

  2. wait方法是Object类的实例方法,它可以让当前线程暂停执行,并释放对象的锁资源。
    这样,其他线程就可以进入同步代码块或方法。当其他线程调用同一个对象的notify()或notifyAll()方法时,等待的线程会被唤醒并继续执行。

下面是一个简单的案例代码:

public class SleepWaitExample {public static void main(String[] args) {Object lock = new Object();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (lock) {try {System.out.println("线程1开始执行");Thread.sleep(3000); // 线程1休眠3秒System.out.println("线程1结束执行");} catch (InterruptedException e) {e.printStackTrace();}}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (lock) {try {System.out.println("线程2开始执行");lock.wait(); // 线程2等待System.out.println("线程2结束执行");} catch (InterruptedException e) {e.printStackTrace();}}}});t1.start();try {Thread.sleep(2000); // 主线程休眠2秒,让线程1先执行} catch (InterruptedException e) {e.printStackTrace();}t2.start();}
}

在这个例子中,我们创建了两个线程t1和t2。

线程t1使用sleep方法休眠3秒,而线程t2使用wait方法等待。

当线程t1执行完毕后,主线程休眠2秒,

然后启动线程t2。

由于线程t2使用了wait方法,它会释放lock对象的锁资源,使得主线程可以继续执行。当主线程调用lock.notify()方法时,线程t2会被唤醒并继续执行。

12、什么是对线程优先级

在Java中,线程优先级是一个整数值,范围从1到10。其中,1代表最低优先级,10代表最高优先级,而默认的线程优先级是5。这个优先级只是表示线程执行的先后顺序,但并不能保证高优先级的线程一定会在低优先级的线程前执行。

下面是一个简单的设置线程优先级的案例代码:

public class SimplePriorities extends Thread {private int countDown = 5;public void run() {while(true) {countDown--;if(countDown == 0) {return;}}}public static void main(String[] args) {Thread t1 = new SimplePriorities();t1.setPriority(10); // 设置线程t1的优先级为最高Thread t2 = new SimplePriorities();t2.setPriority(1); // 设置线程t2的优先级为最低t1.start(); // 启动线程t1t2.start(); // 启动线程t2}
}

13、什么是对线程优先级

在Java中,后台线程也被称为守护线程(Daemon Thread),它是一种在后台提供公共服务的线程,通常用于执行一些周期性或支持性任务。

例如,Java的垃圾回收器就是一个典型的守护线程,它在程序运行过程中自动回收不再使用的内存,无需程序员干预。

后台线程的生命周期并不取决于它自身,而是由所有非后台线程(也称为前台线程)决定的。当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中所有的后台线程。

因此,只要有任何非后台线程还在运行,程序就不会结束。

创建后台线程的方式是将目标线程对象的 setDaemon() 方法设置为 true。

以下是一个简单的案例代码:

public class SimplePriorities extends Thread {private int countDown = 5;public void run() {while(true) {countDown--;if(countDown == 0) {return;}}}public static void main(String[] args) {Thread t1 = new SimplePriorities();t1.setDaemon(true); // 设置线程t1为后台线程Thread t2 = new SimplePriorities();t2.setDaemon(true); // 设置线程t2为后台线程t1.start(); // 启动线程t1t2.start(); // 启动线程t2try {TimeUnit.MILLISECONDS.sleep(175); // 主线程睡眠175毫秒} catch (InterruptedException e) {e.printStackTrace();}System.out.println("所有后台线程都已经结束"); // 主线程打印信息}
}

14、sleep,yiled,wait,join 对比

Java 中的 sleep(), yield(), wait()join() 都是多线程中常用的控制线程的方法,它们分别用于不同的目的。以下是对这些方法的简要对比以及使用它们的示例代码。

  1. Thread.sleep(long millis): 这个方法使当前线程暂停指定的时间(毫秒)。它不释放任何锁,并且不会抛出 InterruptedException。sleep() 是一个静态方法,属于 java.lang.Thread 类。
public class SleepExample {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("Sleeping...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Woke up after sleeping");}});thread.start();thread.join(); // 等待子线程执行完毕}
}
  1. Thread.yield(): 这个方法暗示当前线程应该放弃 CPU 执行时间给其他优先级相同的线程。
    但是这个操作是不可靠的,因为 Java 虚拟机可以忽略这个请求。
    yield() 是一个静态方法,属于 java.lang.Thread 类。
public class YieldExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {while (true) {System.out.println("Thread 1 running");Thread.yield();}});Thread thread2 = new Thread(() -> {while (true) {System.out.println("Thread 2 running");Thread.yield();}});thread1.start();thread2.start();}
}
  1. Object.wait(): 这个方法使当前线程等待直到另一个线程调用此对象的 notify()notifyAll() 方法,或者超过指定的等待时间。
    该方法必须在同步块或同步方法中调用,否则会抛出 IllegalMonitorStateExceptionwait() 是一个实例方法,属于 java.lang.Object 类。
public class WaitExample {private final Object lock = new Object();public static void main(String[] args) {WaitExample example = new WaitExample();example.run();}public void run() {Thread producer = new Thread(() -> {while (true) {synchronized (lock) {System.out.println("Producing");try {lock.wait(); // 等待消费者消费} catch (InterruptedException e) {e.printStackTrace();}}}});Thread consumer = new Thread(() -> {while (true) {synchronized (lock) {System.out.println("Consuming");lock.notify(); // 唤醒生产者try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}});producer.start();consumer.start();}
}
  1. Thread.join(): 这个方法使当前线程等待目标线程终止。如果两个线程都在同一个进程中运行,那么当其中一个线程结束时,另一个线程就可以继续执行。
    join() 是一个实例方法,属于 java.lang.Thread 类。
public class JoinExample {public static void main(String[] args) {Thread worker = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("Worker is working on task " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});worker.start();try {worker.join(); // 等待工人线程完成工作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Main thread can continue now that the worker is done.");}
}

以上就是对 Java 中 sleep(), yield(), wait()join() 的简单介绍和使用示例。
请注意,在实际编程中,需要根据具体需求选择合适的方法来控制线程行为。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/277661.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

决策树 | 分类树回归树:算法逻辑

目录 一. 决策树(Decision Tree)1. 决策树的构建1.1 信息熵(Entropy)1.1.1 信息量&信息熵 定义1.1.2 高信息熵&低信息熵 定义1.1.3 信息熵 公式 1.2 信息增益(Information Gain)1.2.1 信息增益的计算1.2.2 小节 2. 小节2.1 算法分类2.2 决策树算法分割选择2.3 决策树算…

MechanicalSoup,一个非常实用的 Python 自动化浏览器交互工具库!

目录 前言 什么是 Python MechanicalSoup 库&#xff1f; 核心功能 使用方法 1. 安装 MechanicalSoup 库 2. 创建 MechanicalSoup 客户端 3. 打开网页并与之交互 实际应用场景 1. 网页自动化测试 2. 网络爬虫与数据提取 3. 网页自动化操作 4. 自动化填写和提交多个表单 5.…

柚见十三期(优化)

前端优化 加载匹配功能与加载骨架特效 骨架屏 : vant-skeleton index.vue中 /** * 加载数据 */ const loadData async () > { let userListData; loading.value true; //心动模式 if (isMatchMode.value){ const num 10;//推荐人数 userListData await myA…

django-comment-migrate 模型注释的使用

django-comment-migrate 的使用 django-comment-migrate 是一个 Django 应用&#xff0c;用于将模型注释自动迁移到数据库表注释中。它可以帮助您保持数据库表注释与模型定义的一致性&#xff0c;并提高代码的可读性。 安装 要使用 django-comment-migrate&#xff0c;您需要…

线程是如何在 6 种状态之间转换的

线程是如何在 6 种状态之间转换的 线程的 6 种状态New 新创建Runnable 可运行阻塞状态Blocked 被阻塞Waiting 等待Timed Waiting 限期等待 注意点 主要学习线程是如何在 6 种状态之间转换。 线程的 6 种状态 就像生物从出生到长大、最终死亡的过程一样&#xff0c;线程也有自己…

搭建Hadoop3.x完全分布式集群

零、资源准备 虚拟机相关&#xff1a; VMware workstation 16&#xff1a;虚拟机 > vmware_177981.zipCentOS Stream 9&#xff1a;虚拟机 > CentOS-Stream-9-latest-x86_64-dvd1.iso Hadoop相关 jdk1.8&#xff1a;JDK > jdk-8u261-linux-x64.tar.gzHadoop 3.3.6&am…

Netty架构详解

文章目录 概述整体结构Netty的核心组件逻辑架构BootStrap & ServerBootStrapChannelPipelineFuture、回调和 ChannelHandler选择器、事件和 EventLoopChannelHandler的各种ChannelInitializer类图 Protocol Support 协议支持层Transport Service 传输服务层Core 核心层模块…

第七节:Vben Admin权限-后端获取路由和菜单

系列文章目录 第一节:Vben Admin介绍和初次运行 第二节:Vben Admin 登录逻辑梳理和对接后端准备 第三节:Vben Admin登录对接后端login接口 第四节:Vben Admin登录对接后端getUserInfo接口 第五节:Vben Admin权限-前端控制方式 第六节:Vben Admin权限-后端控制方式 第七节…

Unity2019.2.x 导出apk 安装到安卓Android12+及以上的系统版本 安装出现-108 安装包似乎无效的解决办法

Unity2019.2.x 导出apk 安装到安卓Android12及以上的系统版本 安装出现-108 安装包似乎无效的解决办法 导出AndroidStudio工程后 需要设置 build.gradle文件 // GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAINbuildscript {repositor…

河马优化算法(HO)-2024年Nature子刊新算法 公式原理详解与性能测评 Matlab代码免费获取

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理简介 一、种群初始化 二、河马在河流或…

【Python编程基础】第一节.Python基本语法(上)

文章目录 前言⼀、Python介绍二、Python环境配置三、Pycharm 书写代码四、Python基本语法 4.1 print 函数的简单使用 4.2 注释 4.3 Python 代码中三种波浪线和 PEP8 4.4 在 cmd 终端中运⾏ Python 代码 4.5 变量 4.6 数据类型 4.7 类型转换…

Python使用openpyxl库或pandas库创建.xlsx格式的Excel文件,并向文件不同的sheet按行或按列写入内容

import openpyxl# 创建-一个Workbook对象 wb openpyxl.Workbook()# 创建多个工作表 sheet1 wb.active sheet1.title "s1"sheet2 wb.create_sheet("s2")# 在不同的工作表中写入数据 sheet1["A1"] Data for Sheet1 sheet1["A2"] D…

HCIP—BGP邻居关系建立实验

BGP的邻居称为&#xff1a;IBGP对等体 EBGP对等体 1.EBGP对等体关系&#xff1a; 位于 不同自治系统 的BGP路由器之间的BGP对等体关系 EBGP对等体一般使用 直连建立 对等体关系&#xff0c;EBGP邻居之间的报文 TTL中值设置为1 两台路由器之间建立EBGP对等体关系&#xff0…

SQLiteC/C++接口详细介绍之sqlite3类(十三)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十二&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十四&#xff09;&#xff08;未发表&#xff09; 40.sqlite3…

【算法】一类支持向量机OC-SVM(1)

【算法】一类支持向量机OC-SVM 前言一类支持向量机OC-SVM 概念介绍示例编写数据集创建实现一类支持向量机OC-SVM完整的示例输出 前言 由于之前毕设期间主要的工具就是支持向量机&#xff0c;从基础的回归和分类到后来的优化&#xff0c;在接触到支持向量机还有一类支持向量机的…

Redis Desktop Manager:一站式Redis数据库管理与优化

Redis Desktop Manager是一款功能强大的Redis桌面管理工具&#xff0c;也被称作Redis可视化工具。以下是其主要的功能特色&#xff1a; 连接管理&#xff1a;Redis Desktop Manager支持连接多个Redis服务器&#xff0c;用户可以在同一界面下管理多个数据库&#xff0c;大大提高…

通用的springboot web jar包执行脚本,释放端口并执行jar包

1、通用的springboot web jar包执行脚本&#xff0c;释放端口并执行jar包&#xff1a; #!/bin/bash set -eDATE$(date %Y%m%d%H%M) # 基础路径 BASE_PATH/data/yitu-projects/yitu-xzhq/sftp # 服务名称。同时约定部署服务的 jar 包名字也为它。 SERVER_NAMEyitu-server # 环境…

数据仓库数据分层详解

数据仓库中的数据分层是一种重要的数据组织方式&#xff0c;其目的是为了在管理数据时能够对数据有一个更加清晰的掌控。以下是数据仓库中的数据分层详解&#xff1a; 原始数据层&#xff08;Raw Data Layer&#xff09;&#xff1a;这是数仓中最底层的层级&#xff0c;用于存…

计算机二级Python题目13

目录 1. 基本题 1.1 基本题1 1.2 基本题2 1.3 基本题3 2. turtle画图 3. 大题 3.1 大题1 3.2 大题2 1. 基本题 1.1 基本题1 lseval(input()) s"" for item in ls:if type(item)type("香山"):s item print(s) 1.2 基本题2 import random random.se…

android MMKV数据持久化缓存集合

前言 最近在使用mmkv缓存的时候 发现没有集合缓存 非常不方便 自己写一个方法 MMKV public class MmkvUtils {private MmkvUtils() {throw new UnsupportedOperationException("u cant instantiate me...");}public static void init() {MMKV.initialize(LeoUtils…