1、JAVA构建线程的所有方式
//定义一个子类,继承Thread类
class MyThread extends Thread {
//重写run()方法
@Override
public void run() {
//线程要执行的逻辑
System.out.println("Hello, I am a thread.");
}
}
//在主方法中创建子类对象,并调用start()方法启动线程
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread(); //创建子类对象
t.start(); //启动线程
}
}
//定义一个类,实现Runnable接口
class MyRunnable implements Runnable {
//实现run()方法
@Override
public void run() {
//线程要执行的逻辑
System.out.println("Hello, I am a thread.");
}
}
//在主方法中创建该类对象,并作为参数传递给Thread类的构造器,创建Thread对象,并调用start()方法启动线程
public class Main {
public static void main(String[] args) {
MyRunnable r = new MyRunnable(); //创建该类对象
Thread t = new Thread(r); //创建Thread对象
t.start(); //启动线程
}
}
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableDemo {
public static void main(String[] args) throws Exception {
// 创建一个Callable任务
MyCallable task = new MyCallable();
// 使用FutureTask包装Callable任务
FutureTask future = new FutureTask<>(task);
// 创建一个线程并执行FutureTask
Thread thread = new Thread(future);
thread.start();
// 获取Callable任务的返回结果
Integer sum = future.get();
System.out.println("The sum is: " + sum);
}
}
// 实现Callable接口的类
class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
}
2、run和start的区别是什么
在java线程中,run和start的区别主要有以下几点:
3、线程池
3.1、ThreadPoolExecutor的使用
ThreadPoolExecutor是一个实现了ExecutorService接口的类,它可以创建和管理一个线程池,执行Runnable或Callable任务
要使用ThreadPoolExecutor,你可以直接创建它的实例,或者使用Executors类的工厂方法来获取它的实例
下面是一个例子,它直接创建了一个ThreadPoolExecutor的实例,并设置了核心线程数、最大线程数、空闲线程存活时间、任务队列和拒绝策略
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorExample {
public static void main(String[] args) {
// 创建一个有四个核心线程、六个最大线程、一秒空闲存活时间、有十个容量的阻塞队列和默认拒绝策略的线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 6, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
// 提交十五个打印任务
for (int i = 0; i < 15; i++) {
executor.execute(new PrintTask("Task " + i));
}
// 关闭线程池
executor.shutdown();
}
}
// 定义一个打印任务类,实现Runnable接口
class PrintTask implements Runnable {
private String name;
public PrintTask(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + " is running on " + Thread.currentThread().getName());
}
}
输出结果如下图所示
你可以看到,当提交第十二个任务时,由于队列已满,而且没有空闲的线程,所以触发了拒绝策略,抛出了RejectedExecutionException异常。这说明你需要根据你的任务数量和性能要求来合理地配置线程池的参数。
3.2 FixedThreadPool线程池的使用例子
FixedThreadPool的核心线程数和最大线程数都被设置成相同的固定值。
使用无界队列LinkedBlocking作为线程池的工作队列,队列的容量为Integer.MAXVALUE,由于使用无界队列,线程池并不会拒绝任务。
FixedThreadPool是一种线程池,它可以创建一个固定数量的线程来执行任务。如果所有的线程都在忙,那么新的任务就会在队列中等待。如果有空闲的线程,那么队列中的任务就会被分配给它们
要使用FixedThreadPool,你可以使用Executors类的newFixedThreadPool方法来创建一个ExecutorService实例,然后调用它的execute或submit方法来提交Runnable或Callable任务
下面是一个简单的例子,它创建了一个有两个线程的FixedThreadPool,并提交了五个打印任务
执行流程如下:
1、如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务
2、在线程池中完成预热之后,将任务加入任务队列当中
3、线程执行完1中的任务后,会不断从任务队列中拿取任务执行
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
// 创建一个有两个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 提交五个打印任务
for (int i = 0; i < 5; i++) {
executor.execute(new PrintTask("Task " + i));
}
// 关闭线程池
executor.shutdown();
}
}
// 定义一个打印任务类,实现Runnable接口
class PrintTask implements Runnable {
private String name;
public PrintTask(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + " is running on " + Thread.currentThread().getName());
}
}
输出如下
你可以看到,只有两个线程在交替执行五个任务。这样可以避免创建过多的线程,提高资源利用率和性能。
SingleThreadExecutor是一种ExecutorService,它使用一个单独的线程来执行所有提交的任务。它类似于一个线程数为1的FixedThreadPool,但是它有一些额外的特性,比如保证任务的顺序执行和维护一个隐藏的任务队列使用SingleThreadExecutor可以避免多线程的同步问题,比如在访问共享文件系统时2下面是一个使用SingleThreadExecutor的例子:
同样,SingleThreadExecutor采用的也是无界队列LinkedBlockingQueue作为线程池的工作队列。同样不会拒绝任务。
1、如果当前运行线程数少于核心线程数,则创建一个新线程来执行任务
2、当线程池完成预热之后,将任务加入无界队列中
3、线程执行完1中的任务后,会从任务队列中拿任务来继续执行
// 创建一个SingleThreadExecutor
ExecutorService executor = Executors.newSingleThreadExecutor ();
// 提交三个任务
executor.execute (new Runnable () {
@Override
public void run () {
System.out.println ("Task 1");
}
});
executor.execute (new Runnable () {
@Override
public void run () {
System.out.println ("Task 2");
}
});
executor.execute (new Runnable () {
@Override
public void run () {
System.out.println ("Task 3");
}
});
// 关闭executor
executor.shutdown ();
输出结果如下
CachedThreadPool是一个可缓存的线程池,它的工作机制如下:
代码示例 :
// 创建一个可缓存的线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 定义一个简单的任务
Runnable task = () -> {
System.out.println("Hello from " + Thread.currentThread().getName());
};
// 提交10个任务到线程池
for (int i = 0; i < 10; i++) {
cachedPool.execute(task);
}
// 关闭线程池
cachedPool.shutdown();
CachedThreadPool适合执行大量的短期小任务,或者负载较轻的服务器。但是要注意控制并发的任务数,否则可能会创建过多的线程,导致性能问题。另外,CachedThread不适合执行长时间或不确定时间的任务,例如IO操作。
3.5 ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor是一个可以执行定时任务或周期性任务的线程池,它的工作机制如下:
代码示例:
// 创建一个核心线程数为2的定时线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);
// 定义一个简单的任务
Runnable task = () -> {
System.out.println("Hello from " + Thread.currentThread().getName());
};
// 在延迟5秒后执行一次任务
scheduledPool.schedule(task, 5, TimeUnit.SECONDS);
// 在延迟10秒后开始周期性地每隔15秒执行一次任务
scheduledPool.scheduleAtFixedRate(task, 10, 15, TimeUnit.SECONDS);
// 在延迟20秒后开始周期性地每隔10秒加上上次任务执行时间执行一次任务
scheduledPool.scheduleWithFixedDelay(task, 20, 10, TimeUnit.SECONDS);
ScheduledThreadPoolExecutor适合执行需要定时或周期性触发的任务,例如定时清理缓存、定时发送消息等但是要注意控制并发的任务数和执行时间,否则可能会导致任务堆积或错过预期的触发时间。另外,ScheduledThreadPoolExecutor不支持修改已经提交的定时或周期性任务的触发时间或频率
AQS队列是一个双向链表,用来存放等待获取锁的线程节点。每个节点有一个waitStatus属性,表示节点的等待状态,有以下几种取值:
根据锁的模式,AQS队列有两种功能:独占和共享。
因此,AQS队列中的节点并不是一直在自旋,而是根据自己的waitStatus和锁的模式来决定是否自旋、阻塞或者尝试获取锁。