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

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

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

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

// 任务类
public class MyRunnable implements Runnable {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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也是一种Runnable
        MyCallable 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> {

    @Override
    public 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 {
    @Override
    public 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;
    }

    @Override
    public 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 {
    @Override
    public 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() {
                @Override
                public 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() {
                @Override
                public 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() {
                @Override
                public 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 {
    @Override
    public 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() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("线程1:" + i);
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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(); // 启动线程t1
       t2.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(); // 启动线程t1
       t2.start(); // 启动线程t2
       try {
           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() 的简单介绍和使用示例。
请注意,在实际编程中,需要根据具体需求选择合适的方法来控制线程行为。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins实现CICD(4)_Jenkins和gitlab进行交互

文章目录 一、实现功能二、操作思路三、插件安装四、jenkins与gitlab集成配置2.1、需求2.2、gitlab生成 API认证token2.2.1、创建token 2.3、jenkins使用gitlab API通信2.3.1、创建凭据2.3.2、查看创建结果 2.4、jenkins 集成 Gitlab2.4.1、配置2.4.2、操作流程 参考&#xff1…

XDAG节点版本更新(0.6.5升级到0.7.0)

1、拉取最新的xdagj源码 mkdir /root/xdagj-0.7.0 && cd /root/xdagj-0.7.0 git clone https://github.com/XDagger/xdagj.git cd xdagj mvn clean package -Dmaven.test.skiptrue2、创建新的数据目录并解压程序包 mkdir /data/docker-compose/xdagj-7.0/bin -p cd /…

Linux使用git命令行教程

. 个人主页&#xff1a;晓风飞 专栏&#xff1a;数据结构|Linux|C语言 路漫漫其修远兮&#xff0c;吾将上下而求索 文章目录 git安装git仓库的创建.git 文件添加文件git 三板斧(add,commit,push)解释拓展git log.gitignore git安装 首先输入git --version看看有没有安装git 如…

STM32F4+薄膜压力传感器(FSR)AO模拟输出程序ADC模数转换器详解

前言&#xff1a;博主在使用STM32F4加薄膜压力传感器用来测量压力时&#xff0c;发现给的例程只有STM32F1系列的&#xff0c;而STM32F4系列库函数程序不太一致&#xff0c;博主实战解决了该问题&#xff0c;用STM32F4标准库开发。有关ADC模数转换器的详细知识点详情点击我的博文…

【深度长文】聊一聊 Java AbstractQueuedSynchronizer 以及在 ReentrantLock 中的应用

文章目录 AQS 原理简述内部数据结构公平锁 vs. 非公平锁ReentrantLock 非公平锁ReentrantLock 公平锁 AQS 源码分析加锁过程addWaiteracquireQueuedshouldParkAfterFailedAcquirecancelAcquire 解锁过程unparkSuccessor AbstractQueuedSynchronizer (AQS) 是 Java 并发包中&…

【AUTOSAR】【通信栈】Fls

AUTOSAR专栏——总目录-CSDN博客文章浏览阅读592次。本文主要汇总该专栏文章,以方便各位读者阅读。https://blog.csdn.net/qq_42357877/article/details/132072415?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132072415%22…

【电路笔记】-MOSFET作为开关

MOSFET 作为开关 文章目录 MOSFET 作为开关1、概述2、MOSFET特性曲线2.1 截住区域2.2 饱和区域3、MOSFET作为开关的示例4、功率MOSFET电机控制5、P沟道MOSFET作为开关6、互补MOSFET作为开关电机控制器当 MOSFET 在截止区和饱和区之间工作时,MOSFET 是非常好的电子开关,用于控…

RK3588 Buildroot 增加本地模块(单独编译/加入系统配置)

前言 本文基于 RK3588 Buildroot 编写. 在RK3588开发板环境下&#xff0c;开发者通常利用Buildroot来定制适合RK3588芯片特性的嵌入式Linux系统。通过Buildroot&#xff0c;开发者能够根据实际需求裁剪系统组件、添加特定驱动、配置内核特性&#xff0c;并集成用户应用程序&a…

哪里有视频素材网站免费下载?高清烟花视频素材哪里有?

如果你在寻找那些能点亮夜空的绚丽烟花视频素材&#xff0c;或者无水印的高清视频素材&#xff0c;那下面这些资源网站将会是你的宝库。今天&#xff0c;我要分享给你一些最佳的无水印视频素材下载网站&#xff0c;让你的视频制作闪耀起来。 1.蛙学府 这个网站是视频创作者的天…

C++之入门一

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

网安渗透攻击作业(4)

Unload-labs-01 function checkFile() { var file document.getElementsByName(upload_file)[0].value; if (file null || file "") { alert("请选择要上传的文件!"); return false; } //定义允许上传的文件类型 v…

PHP反序列化--引用

一、引用的理解&#xff1a; 引用就是给予一个变量一个恒定的别名。 int a 10; int b &a; a 20; cout<<a<<b<<endl; 输出结果 : a20、b20 二、靶场复现&#xff1a; <?php highlight_file(__FILE__); error_reporting(0); include("flag.p…

10大漏洞评估和渗透测试工具【附安装包】

1、Netsparker Security Scanner 专为企业设计的强大的漏洞扫描和管理工具&#xff0c;它可以检测和利用 SQL 注入和 XSS 等漏洞。 https://www.netsparker.com/product/ 2、Acunetix Scanner 针对中小型企业的 Web 应用程序漏洞扫描程序&#xff0c;但也可以扩展到更大的组…

Jenkins实现CICD(3)_Jenkins连接到git

文章目录 1、如何完成上述操作&#xff0c;并且不报如下错&#xff1a;2、连接不上git&#xff0c;操作如下&#xff1a;3、将上边产生的3个文件拷贝到&#xff1a;C:\Windows\System32\config\systemprofile\.ssh4、新建下图凭证&#xff1a;创建步骤&#xff1a; 5、公钥填到…

搜索练习(地下城主,查找倍数)

地下城主 思路&#xff1a;这个其实就是bfs的板子&#xff0c;但是和以往的bfs不同&#xff0c;这个bfs适用于三维空间&#xff0c;也就是说多出一维需要进行搜索&#xff1a; 犯下的错误&#xff1a;在bfs的输出中我写成了cout<<q[tail].step1<<endl; 由于在之前…

机器人路径规划:基于流场寻路算法(Flow Field Pathfinding)的机器人路径规划(提供Python代码)

流场寻路算法(Flow Field Pathfinding)是一种基于流体动力学理论的路径规划算法&#xff0c;它模拟了流体在空间中的流动&#xff0c;并利用流体的运动特性来指导路径的选择。下面是流场寻路算法的基本介绍及算法描述&#xff1a; 1. 基本介绍 流场寻路算法通过将环境划分为网…

JWT原理

JWT 介绍 JWT&#xff08;JSON Web Token&#xff09;是一个开放标准&#xff08;RFC 7519&#xff09;&#xff0c;它定义了一种简洁的、自包含的方法用于通信双方之间以 JSON 对象的形式安全地传输信息。这种信息可以被验证和信任&#xff0c;因为它是数字签名的。JWT通常用于…

洛谷P1100 高低位交换

#先看题目 题目描述 给出一个小于 的非负整数。这个数可以用一个 32 位的二进制数表示&#xff08;不足 32 位用 0 补足&#xff09;。我们称这个二进制数的前 16 位为“高位”&#xff0c;后 16 位为“低位”。将它的高低位交换&#xff0c;我们可以得到一个新的数。试问这…

算法之前缀和

题目1: 【模板】一维前缀和&#xff08;easy&#xff09; 方法一: 暴力解法, 时间复杂度O(n*q), 当n10^5, q 10^5, 时间复杂度为O(10^10), 会超时. 方法二: 前缀和: 快速求出数组中某一段连续区间的和. 第一步: 预处理出来一个前缀和数组dp: 1. dp[i]表示区间[1,i]里所有元…

ConcurrentHashMap的相关介绍和使用

概述 ConcurrentHashMap是Java中提供的一个关于线程安全的哈希表实现&#xff0c;他是java.util.concurrent包的一部分&#xff0c;允许多个读操作并发进行&#xff0c;提高了并发环境下的性能。ConcurrentHashMap实现了ConcurrentMap接口&#xff0c;故而他也有ConcurrentMap…