Thread
Java中的Thread是用于实现多线程编程的类。通过创建Thread对象并调用其start()方法,可以启动一个新的线程并执行指定的代码。
Thread使用
以下是一个简单的示例,展示了如何使用Thread类创建和启动一个线程:
java
public class MyThread extends Thread {
public void run() {
// 线程要执行的代码
System.out.println("Hello from a thread!");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
在上面的例子中,我们创建了一个继承自Thread类的自定义线程类MyThread。在run()方法中定义了线程要执行的代码,这里只是简单地打印一条消息。在main()方法中,我们创建了一个MyThread对象,并调用其start()方法来启动线程。
启动线程后,它会在后台执行run()方法中的代码。注意,不要直接调用run()方法,而是使用start()方法来启动线程,这样才能实现多线程并发执行的效果。
需要注意的是,Java中还有其他实现多线程的方式,比如实现Runnable接口、使用线程池等。但Thread类是最基本和常用的多线程编程方式之一。
Thread的一些缺点包括:
-
多线程编程复杂性:使用多线程编程需要考虑线程同步、死锁、竞态条件等问题,这增加了程序设计和调试的复杂性。
-
内存消耗:每个线程都需要一定的内存来维护线程栈、程序计数器等信息,当线程数量过多时,会增加内存消耗。
-
上下文切换开销:线程之间的切换需要保存和恢复上下文信息,这会带来一定的开销。
-
线程安全问题:多线程环境下,共享资源的访问需要进行同步,否则可能会出现数据竞争和不一致的问题。
-
难以调试:多线程程序的调试相对复杂,由于线程的并发执行,问题的复现和定位可能会更加困难。
-
可能导致死锁:如果线程之间存在循环等待资源的情况,就可能导致死锁,使得程序无法继续执行。
总之,虽然Java Thread提供了多线程编程的便利性,但也存在一些缺点,需要开发人员在设计和实现时注意避免潜在的问题。
线程池(ExecutorService、ThreadPool)
newCachedThreadPool
newCachedThreadPool
是Java中的一个线程池创建方法,它返回一个ThreadPoolExecutor
对象,该对象可以用于执行多个任务。它是一个可缓存的线程池,可以根据需要创建新的线程,如果线程池中的线程空闲时间超过指定的时间(默认为60秒),则会被终止并移除。当任务到达时,如果线程池中有空闲线程,则会立即执行任务;如果没有空闲线程,则会创建新的线程来执行任务。适用于执行大量的短期异步任务的场景,不适用于长期运行的任务。
使用示例:
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolExample {
public static void main(String[] args) {
// 创建一个可缓存的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交任务给线程池执行
for (int i = 0; i < 10; i++) {
final int taskId = i;
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("Task " + taskId + " is being executed.");
}
});
}
// 关闭线程池
executorService.shutdown();
}
}
在上面的示例中,我们首先使用Executors.newCachedThreadPool()
方法创建一个可缓存的线程池。然后,我们使用execute()
方法向线程池提交了10个任务,每个任务都是一个Runnable
对象,用于打印任务的编号。最后,我们调用shutdown()
方法关闭线程池。
newFixedThreadPool
newFixedThreadPool
是Java中的一个线程池创建方法。它创建一个固定大小的线程池,其中线程的数量是固定的,不会根据任务的数量进行动态调整。
使用 newFixedThreadPool
方法创建的线程池可以同时执行指定数量的任务,当有新的任务提交时,如果线程池中有空闲的线程,则会立即执行;如果线程池中没有空闲的线程,则新的任务会被放入任务队列中等待执行,直到有线程空闲为止。
这种线程池适用于需要控制并发线程数量的场景,可以有效地控制系统资源的使用,避免因为线程过多而导致系统负载过高的问题。
使用示例:
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个固定大小为5的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务给线程池
for (int i = 0; i < 10; i++) {
final int taskNum = i;
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("正在执行任务 " + taskNum);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务 " + taskNum + " 执行完毕");
}
});
}
// 关闭线程池
executor.shutdown();
}
}
上述示例中,我们首先通过Executors.newFixedThreadPool(5)
创建了一个固定大小为5的线程池。然后,我们通过executor.execute()
方法提交了10个任务给线程池进行执行。每个任务都会打印出自己的任务编号,并在执行完毕后打印出执行完毕的消息。最后,我们通过executor.shutdown()
方法关闭线程池。
使用线程池可以更好地管理和控制线程的执行,提高程序的性能和效率。newFixedThreadPool
方法创建的线程池具有固定大小,适用于需要控制并发线程数量的场景。
newScheduledThreadPool
newScheduledThreadPool
是Java中的一个方法,用于创建一个可调度的线程池。它返回一个ScheduledExecutorService
对象,可以用来执行定时任务和周期性任务。
使用示例:
java
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
// 执行定时任务
executor.schedule(() -> {
System.out.println("定时任务执行");
}, 1, TimeUnit.SECONDS);
// 执行周期性任务
executor.scheduleAtFixedRate(() -> {
System.out.println("周期性任务执行");
}, 0, 2, TimeUnit.SECONDS);
// 关闭线程池
executor.shutdown();
}
}
在上述示例中,我们通过Executors.newScheduledThreadPool(5)
创建了一个可调度的线程池,其中参数5表示线程池的大小为5。然后,我们可以使用返回的ScheduledExecutorService
对象执行定时任务和周期性任务。最后,通过调用executor.shutdown()
方法来关闭线程池。
newSingleThreadExecutor
newSingleThreadExecutor
是 Java 中的一个线程池创建方法,用于创建一个只有一个线程的线程池。它的定义如下:
java
ExecutorService executor = Executors.newSingleThreadExecutor();
这个线程池只会创建一个线程来执行任务,当这个线程执行完一个任务后,会接着执行下一个任务。如果有多个任务提交给这个线程池,它们会按照提交的顺序依次执行。
使用 newSingleThreadExecutor
可以保证任务按照顺序执行,适用于需要顺序执行的场景,比如需要按照任务的提交顺序来处理数据的情况。
示例代码:
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
// 创建一个单线程的线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交任务给线程池执行
executor.submit(() -> {
System.out.println("Task 1 is running");
});
executor.submit(() -> {
System.out.println("Task 2 is running");
});
// 关闭线程池
executor.shutdown();
}
}
在上面的示例中,我们首先使用Executors.newSingleThreadExecutor()
方法创建了一个单线程的线程池。然后,我们使用executor.submit()
方法提交了两个任务给线程池执行。最后,我们调用executor.shutdown()
方法关闭线程池。
由于newSingleThreadExecutor
创建的是一个单线程的线程池,所以任务会按照提交的顺序依次执行。这样可以保证任务之间的顺序性,适用于需要按照顺序执行的场景。
Java提供的四种线程池的优点
Java提供了四种线程池,分别是FixedThreadPool、CachedThreadPool、ScheduledThreadPool和SingleThreadPool。它们各自有不同的优点:
-
FixedThreadPool:固定大小的线程池。它适用于需要控制并发线程数的场景,可以限制线程的数量,避免资源耗尽。适用于执行长期的任务,效率高。
-
CachedThreadPool:可缓存的线程池。它适用于执行大量的短期任务,可以根据需要自动创建新的线程,线程池的大小可以根据任务的多少自动调整。适用于执行大量的耗时较短的任务,灵活性高。
-
ScheduledThreadPool:定时任务线程池。它适用于需要定时执行任务的场景,可以按照指定的时间间隔周期性地执行任务。适用于需要定时执行任务的场景,如定时备份、定时统计等。
-
SingleThreadPool:单线程池。它适用于需要保证任务按照顺序执行的场景,所有任务都在同一个线程中按照指定的顺序执行。适用于需要保证任务按照顺序执行的场景,如消息队列等。
这四种线程池都是通过线程池Executor框架提供的,可以有效地管理和复用线程,提高程序的性能和效率。
自定义线程池
自定义线程池步骤:
- 创建一个线程池类,可以命名为
CustomThreadPool
。 - 在该类中,需要定义以下属性:
ThreadPoolExecutor
对象:用于管理线程池的执行和调度。int
类型的corePoolSize
:指定线程池的核心线程数,即线程池中保持活动状态的线程数。int
类型的maximumPoolSize
:指定线程池的最大线程数,即线程池中允许的最大线程数。long
类型的keepAliveTime
:指定线程池中非核心线程的闲置超时时间。BlockingQueue<Runnable>
类型的workQueue
:用于存放待执行的任务的阻塞队列。
- 在
CustomThreadPool
类的构造方法中,初始化线程池对象,并设置相关属性。 - 提供方法来提交任务到线程池中执行,可以命名为
submitTask
方法。该方法接受一个Runnable
类型的任务作为参数,并将任务提交到线程池中执行。 - 可以根据需要,提供其他方法来管理线程池,如获取当前活动线程数、获取线程池中的任务数量等。
示例代码:
java
import java.util.concurrent.*;
public class CustomThreadPool {
private ThreadPoolExecutor threadPool;
public CustomThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, BlockingQueue<Runnable> workQueue) {
threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, workQueue);
}
public void submitTask(Runnable task) {
threadPool.submit(task);
}
public int getActiveThreadCount() {
return threadPool.getActiveCount();
}
public int getTaskCount() {
return threadPool.getQueue().size();
}
}
使用自定义线程池的示例代码:
java
public class Main {
public static void main(String[] args) {
// 创建一个阻塞队列作为任务队列
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
// 创建自定义线程池对象
CustomThreadPool threadPool = new CustomThreadPool(5, 10, 5000, workQueue);
// 提交任务到线程池中执行
for (int i = 0; i < 20; i++) {
final int taskNum = i;
threadPool.submitTask(() -> {
System.out.println("Task " + taskNum + " is running.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskNum + " is finished.");
});
}
// 输出线程池中的活动线程数和任务数量
System.out.println("Active Thread Count: " + threadPool.getActiveThreadCount());
System.out.println("Task Count: " + threadPool.getTaskCount());
// 关闭线程池
threadPool.shutdown();
}
}
以上代码示例中,创建了一个自定义线程池对象threadPool
,并提交了20个任务到线程池中执行。然后通过getActiveThreadCount
方法和getTaskCount
方法获取线程池中的活动线程数和任务数量。最后调用shutdown
方法关闭线程池。