在本文中,我们将为您详细介绍JAVA:不使用线程怎么办?的相关知识,并且为您解答关于java线程不执行的疑问,此外,我们还会提供一些关于javanotify只唤醒一个,其他未唤醒的线程怎么办?一直阻塞
在本文中,我们将为您详细介绍JAVA:不使用线程怎么办?的相关知识,并且为您解答关于java线程不执行的疑问,此外,我们还会提供一些关于java notify 只唤醒一个,其他未唤醒的线程怎么办?一直阻塞着吗?、Java 使用线程发出数据库请求、Java 使用线程池执行若干任务、Java 多线程 -- 创建和使用线程池的有用信息。
本文目录一览:- JAVA:不使用线程怎么办?(java线程不执行)
- java notify 只唤醒一个,其他未唤醒的线程怎么办?一直阻塞着吗?
- Java 使用线程发出数据库请求
- Java 使用线程池执行若干任务
- Java 多线程 -- 创建和使用线程池
JAVA:不使用线程怎么办?(java线程不执行)
为每辆保持状态的汽车创建一个对象。将所有汽车放入列表中,并对其进行200次遍历:
import java.util.Arrays;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class CarGame extends JFrame {
class Car {
private JLabel label;
private int x,y;
public Car(String fname,int x,int y) {
this.x = x;
this.y = y;
label = new JLabel();
label.setIcon(new ImageIcon(fname));
label.setBounds(x,y,100,100);
add(label);
}
public void move()
{
x += 10 * Math.random();
label.setBounds(x,100);
repaint();
}
}
public CarGame() {
setTitle("Car Race");
setSize(600,200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
List<Car> cars=Arrays.asList(new Car("car1.gif",0),new Car("car2.gif",50),new Car("car3.gif",100));
setVisible(true);
for(int i=0;i<200;i++)
{
cars.forEach(car->car.move());
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
CarGame t = new CarGame();
}
}
您将始终只有一个线程。
备注:为此目的使用Thread.sleep()并不是一个好主意,因为它将阻止线程执行其他工作。最好使用每100毫秒触发一次并调用cars.foreach(…)
的计时器。当然,这将导致第二个线程。一旦第一个(或最后一个)赛车到达终点,就可以取消计时器。 (感谢@JoopEggen的有效评论)
为了完整性(并且这样做很有趣):没有线程但带有:
的版本- javax.swing.Timer,该计时器会在所有汽车停驶后停止。
- 计算每辆车的最终位置
- 为那些没有汽车gif的人显示文本。
import java.util.Arrays;
import java.util.List;
import javax.swing.Timer;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class CarGame extends JFrame {
int width = 600;
int position=1;
class Car {
private JLabel label;
private int x,y;
public Car(String fname,int y) {
this.x = x;
this.y = y;
label = new JLabel(fname+">");
label.setIcon(new ImageIcon(fname+".gif"));
label.setBounds(x,100);
add(label);
}
public boolean move() {
if (!finished()) {
x += 10 * Math.random();
label.setBounds(x,100);
if(finished())
label.setText(label.getText()+"["+(position++)+"]");
}
return finished();
}
public boolean finished() {
return x > width;
}
}
public CarGame() {
setTitle("Car Race");
setSize(width+100,200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
List<Car> cars = Arrays.asList(new Car("car1",new Car("car2",new Car("car3",100));
setVisible(true);
Timer timer=new Timer(100,null);
timer.addActionListener(e-> {
// count the number of non-finished cars
if(cars.stream().map(car -> car.move()).filter(b->!b).count()==0)
timer.stop();
});
timer.start();
}
public static void main(String[] args) {
new CarGame();
}
}
java notify 只唤醒一个,其他未唤醒的线程怎么办?一直阻塞着吗?
java notify 只唤醒一个,其他未唤醒的线程怎么办?一直阻塞着吗?
Java 使用线程发出数据库请求
如何解决Java 使用线程发出数据库请求?
线程和JavaFX有两个基本规则:
- 任何修改或访问场景图一部分的节点状态的代码都必须在JavaFX应用程序线程上执行。某些其他操作(例如,创建
new Stage
)也受此规则约束。 - 任何可能花费很长时间运行的代码都应在后台线程上执行(即不在FX Application线程上执行)。
第一条规则的原因是,像大多数UI工具包一样,编写框架时对场景图的元素状态没有任何同步。添加同步会导致性能损失,这对于UI工具包来说是一笔过高的费用。因此,只有一个线程可以安全地访问此状态。由于UI线程(JavaFX的FX应用程序线程)需要访问此状态才能呈现场景,因此FX
Application
线程是唯一可以访问“实时”场景图状态的线程。在JavaFX 8和更高版本中,受此规则约束的大多数方法都会执行检查,并在违反该规则时抛出运行时异常。(这与Swing
相反,在Swing中,你可以编写“非法”代码,并且看起来可能运行良好,但实际上在任意时间都容易出现随机且无法预测的故障。)这是IllegalStateException
你看到的原因:你是courseCodeLbl.setText(...)
从FX Application Thread
之外的其他线程进行调用。
第二条规则的原因是,FX Application Thread
不仅负责处理用户事件,还负责渲染场景。因此,如果你在该线程上执行了长时间运行的操作,则在该操作完成之前,UI才会呈现,并且将对用户事件无响应。虽然这不会产生异常或导致损坏的对象状态(如违反规则1那样),但(最多)它会带来不良的用户体验。
因此,如果你有一个长时间运行的操作(例如访问数据库),需要在完成时更新UI,则基本计划是在后台线程中执行长时间运行的操作,并在操作完成时返回操作结果完成,然后在UI(FX应用程序)线程上安排对UI的更新。所有单线程UI工具箱都有执行此操作的机制:在JavaFX中,你可以通过在FX Application Thread上Platform.runLater(Runnable r)
执行调用来实现r.run()
。(在Swing中,你可以调用在AWT事件分配线程上SwingUtilities.invokelater(Runnable r)
执行r.run()
。)JavaFX(请参见本答案后面的内容)还提供了一些高级API,用于管理与FX Application Thread
的通信。
多线程通用良好实践
使用多个线程的最佳实践是将要在“用户定义”线程上执行的代码构造为具有某种固定状态初始化的对象,并具有执行该操作的方法,并在完成时返回一个对象代表结果。对于初始化状态和计算结果使用不可变的对象是非常理想的。这里的想法是尽可能消除任何可变状态从多个线程可见的可能性。从数据库访问数据非常适合以下习惯用法:你可以使用数据库访问的参数(搜索项等)来初始化“ worker”对象。执行数据库查询并获取结果集,使用该结果集填充域对象的集合,然后最后返回该集合。
在某些情况下,有必要在多个线程之间共享可变状态。当绝对必须这样做时,你需要仔细同步对该状态的访问,以避免观察到状态不一致的状态(还有其他更细微的问题需要解决,例如状态的活跃性等)。强烈建议在需要时使用高级库来为你管理这些复杂性。
JavaFX提供了一个并发API,专门用于在后台线程中执行代码,而该API专门用于在代码执行完成时(或执行期间)更新JavaFX UI。此API旨在与java.util.concurrentAPI
进行交互,该API提供了用于编写多线程代码的常规工具(但没有UI挂钩)。中的键类javafx.concurrent
是Task,它表示打算在后台线程上执行的单个一次性工作单元。此类定义了一个抽象方法,call()该方法不带任何参数,返回结果并可能引发检查的异常。用调用的方法Task实现。Runnablerun()call()Task
还具有被保证更新状态的FX应用程序线程的方法,例如作为一个集合updateProgress(...)
,updateMessage(...)
等,这定义了一些可观察到的性质(例如state
和value
):听众对这些属性将被通知的FX应用程序线程的变化。最后,有一些方便的方法来注册处理程序(setonSucceeded(...),setonFailed(...)
等); 通过这些方法注册的所有处理程序也将在FX Application Thread
上调用。
因此,从数据库检索数据的一般公式为:
- 创建一个Task来处理对数据库的调用。
- 使用Task执行数据库调用所需的任何状态初始化。
- 实现任务的call()方法以执行数据库调用,并返回调用结果。
- 向任务注册一个处理程序,以在完成后将结果发送到UI。
- 在后台线程上调用任务。 对于数据库访问,我强烈建议将实际的数据库代码封装在一个对UI一无所知的单独类中(数据访问对象设计模式)。然后,让任务调用数据访问对象上的方法。
因此,你可能会有一个这样的DAO类(请注意,这里没有UI代码):
public class WidgetDAO {
// In real life, you might want a connection pool here, though for
// desktop applications a single connection often suffices:
private Connection conn ;
public WidgetDAO() throws Exception {
conn = ... ; // initialize connection (or connection pool...)
}
public List<Widget> getWidgetsByType(String type) throws sqlException {
try (PreparedStatement pstmt = conn.prepareStatement("select * from widget where type = ?")) {
pstmt.setString(1, type);
ResultSet rs = pstmt.executeQuery();
List<Widget> widgets = new ArrayList<>();
while (rs.next()) {
Widget widget = new Widget();
widget.setName(rs.getString("name"));
widget.setNumberOfBigRedButtons(rs.getString("btnCount"));
// ...
widgets.add(widget);
}
return widgets ;
}
}
// ...
public void shutdown() throws Exception {
conn.close();
}
}
检索一堆小部件可能会花费很长时间,因此来自UI类(例如,控制器类)的任何调用都应在后台线程上进行调度。控制器类可能如下所示:
public class MyController {
private WidgetDAO widgetAccessor ;
// java.util.concurrent.Executor typically provides a pool of threads...
private Executor exec ;
@FXML
private TextField widgetTypeSearchField ;
@FXML
private TableView<Widget> widgetTable ;
public void initialize() throws Exception {
widgetAccessor = new WidgetDAO();
// create executor that uses daemon threads:
exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
}
// handle search button:
@FXML
public void searchWidgets() {
final String searchString = widgetTypeSearchField.getText();
Task<List<Widget>> widgetSearchTask = new Task<List<Widget>>() {
@Override
public List<Widget> call() throws Exception {
return widgetAccessor.getWidgetsByType(searchString);
}
};
widgetSearchTask.setonFailed(e -> {
widgetSearchTask.getException().printstacktrace();
// inform user of error...
});
widgetSearchTask.setonSucceeded(e ->
// Task.getValue() gives the value returned from call()...
widgetTable.getItems().setAll(widgetSearchTask.getValue()));
// run the task using a thread from the thread pool:
exec.execute(widgetSearchTask);
}
// ...
}
请注意,对(可能)长时间运行的DAO方法的调用是如何包装在一个Task在后台线程中(通过访问器)运行的中的,以防止阻塞UI(上述规则2)。widgetTable.setItems(...)
实际上,使用Task的便捷回调方法setonSucceeded(...)
(满足规则1)在FX Application
线程上执行了对UI()的更新。
在你的情况下,你正在执行的数据库访问将返回单个结果,因此你可能具有类似以下的方法
public class MyDAO {
private Connection conn ;
// constructor etc...
public Course getCourseByCode(int code) throws sqlException {
try (PreparedStatement pstmt = conn.prepareStatement("select * from course where c_code = ?")) {
pstmt.setInt(1, code);
ResultSet results = pstmt.executeQuery();
if (results.next()) {
Course course = new Course();
course.setName(results.getString("c_name"));
// etc...
return course ;
} else {
// maybe throw an exception if you want to insist course with given code exists
// or consider using Optional<Course>...
return null ;
}
}
}
// ...
}
然后你的控制器代码看起来像
final int courseCode = Integer.valueOf(courseId.getText());
Task<Course> courseTask = new Task<Course>() {
@Override
public Course call() throws Exception {
return myDAO.getCourseByCode(courseCode);
}
};
courseTask.setonSucceeded(e -> {
Course course = courseTask.getCourse();
if (course != null) {
courseCodeLbl.setText(course.getName());
}
});
exec.execute(courseTask);
该api文档Task
有更多的例子,包括更新progress
任务的属性进度条…等(有用
解决方法
我试图了解线程如何在Java中工作。这是一个简单的数据库请求,返回一个ResultSet。我正在使用JavaFx。
package application;
import java.sql.ResultSet;
import java.sql.SQLException;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
public class Controller{
@FXML
private Button getCourseBtn;
@FXML
private TextField courseId;
@FXML
private Label courseCodeLbl;
private ModelController mController;
private void requestCourseName(){
String courseName = "";
Course c = new Course();
c.setCCode(Integer.valueOf(courseId.getText()));
mController = new ModelController(c);
try {
ResultSet rs = mController.<Course>get();
if(rs.next()){
courseCodeLbl.setText(rs.getString(1));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// return courseName;
}
public void getCourseNameOnClick(){
try {
// courseCodeLbl.setText(requestCourseName());
Thread t = new Thread(new Runnable(){
public void run(){
requestCourseName();
}
},"Thread A");
t.start();
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这将返回一个异常:
线程“线程A”中的异常java.lang.IllegalStateException:在FX应用程序线程上不;currentThread =线程A
如何正确实现线程,以便每个数据库请求都在第二个线程而不是主线程中执行?
我听说过实现Runnable,但是如何在run方法中调用不同的方法?
以前从未使用过线程,但我认为是时候了。
Java 使用线程池执行若干任务
在执行一系列带有 IO 操作(例如下载文件),且互不相关的异步任务时,采用多线程可以很极大的提高运行效率。线程池包含了一系列的线程,并且可以管理这些线程。例如:创建线程,销毁线程等。本文将介绍如何使用 Java 中的线程池执行任务。
1 任务类型
在使用线程池执行任务之前,我们弄清楚什么任务可以被线程池调用。按照任务是否有返回值可以将任务分为两种,分别是实现 Runnable 的任务类(无参数无返回值)和实现 Callable 接口的任务类(无参数有返回值)。在打代码时根据需求选择对应的任务类型。
1.1 实现 Runnable 接口的类
多线程任务类型,首先自然想到的就是实现 Runnable 接口的类,Runnable 接口提供了一个抽象方法 run,这个方法无参数,无返回值。例如:
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Execute task.");
}
};
或者 Java 8 及以上版本更简单的写法:
Runnable task = ()->{
System.out.println("Execute task.");
};
1.2 实现 Callable 接口的类
于 Runnable 一样 Callable 也只有一个抽象方法,不过该抽象方法有返回值。在实现该接口的时候需要制定返回值的类型。例如:
Callable<String> callableTask = ()-> "finished";
2 线程池类型
java.util.concurrent.Executors 提供了一系列静态方法来创建各种线程池。下面例举出了主要的一些线程池及特性,其它未例举线程池的特性可由下面这些推导出来。
2.1 线程数固定的线程池 Fixed Thread Pool
顾名思义,这种类型线程池线程数量是固定的。如果线程数量设置为 n,则任何时刻该线程池最多只有 n 个线程处于运行状态。当线程池中处于饱和运行状态时,再往线程池中提交的任务会被放到执行队列中。如果线程池处于不饱和状态,线程池也会一直存在,直到 ExecuteService 的 shutdown 方法被调用,线程池才会被清除。
// 创建线程数量为5的线程池。
ExecutorService executorService = Executors.newFixedThreadPool(5);
2.2 可缓存的线程池 Cached Thread Pool
这种类型的线程池初始大小为 0 个线程,随着往池里不断提交任务,如果线程池里面没有闲置线程(0 个线程也表示没有闲置线程),则会创建新的线程,保证没有任务在等待;如果有闲置线程,则复用闲置状态线程执行任务。处于闲置状态的线程只会在线程池中缓存 60 秒,闲置时间达到 60s 的线程会被关闭并移出线程池。在处理大量短暂的(官方说法:short-lived)异步任务时可以显著得提供程序性能。
//创建一个可缓存的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
2.3 单线程池
这或许不能叫线程池了,由于它里面的线程永远只有 1 个,而且自始至终都只有 1 个(为什么说这句话,因为要和 Executors.newFixedThreadPool(1) 区别开来),所以还是叫它 “单线程池把”。你尽可以往单线程池中添加任务,但是每次只执行 1 个,且任务是按顺序执行的。如果前面的任务出现了异常,当前线程会被销毁,但 1 个新的线程会被创建用来执行后面的任务。以上这些和线程数只有 1 个的线程 Fixed Thread Pool 一样。两者唯一不同的是, Executors.newFixedThreadPool(1) 可以在运行时修改它里面的线程数,而 Executors.newSingleThreadExecutor() 永远只能有 1 个线程。至于 “为什么”,我准备专门再写一篇博客通过源代码来分析。
//创建一个单线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
2.4 工作窃取线程池
扒开源码,会发现工作窃取线程池本质是 ForkJoinPool ,这类线程池充分利用 CPU 多核处理任务,适合处理消耗 CPU 资源多的任务。它的线程数不固定,维护的任务队列有多个,当一个任务队列完成时,相应的线程会从其它的任务队列中窃取任务执行,这也意味着任务的开始执行顺序并和提交顺序相同。如果有更高的需求,可以直接通过 ForkJoinPool 获取线程池。
//创建一个工作窃取线程池,使用CPU核数等于机器的CPU核数
ExecutorService executorService = Executors.newWorkStealingPool();
//创建一个工作窃取线程池,使用CPU 3 个核进行计算,工作窃取线程池不能设置线程数
ExecutorService executorService2 = Executors.newWorkStealingPool(3);
2.5 计划任务线程池
计划任务线程池可以按计划执行某些任务,例如:周期性的执行某项任务。
// 获取一个大小为2的计划任务线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
// 添加一个打印当前线程信息计划任务,该任务在3秒后执行
scheduledExecutorService.schedule(() -> { System.out.println(Thread.currentThread()); }, 3, TimeUnit.SECONDS);
// 添加一个打印当前线程信息计划任务,该任务在2秒后首次执行,之后每5秒执行一次。如果任务执行时间超过了5秒,则下一次将会在前一次执行完成之后立即执行
scheduledExecutorService.scheduleAtFixedRate(() -> { System.out.println(Thread.currentThread()); }, 2, 5, TimeUnit.SECONDS);
// 添加一个打印当前线程信息计划任务,该任务在2秒后首次执行,之后每次在任务执行之后5秒执行下一次。
scheduledExecutorService.scheduleWithFixedDelay(() -> { System.out.println(Thread.currentThread()); }, 2, 5, TimeUnit.SECONDS);
// 逐个清除 idle 状态的线程
scheduledExecutorService.shutdown();
// 阻塞,在线程池被关调之前代码不再往下走
scheduledExecutorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
3 使用线程池执行任务
前面提到,任务类型分为有返回值和无返回值的类型,这里的调用也分为有返回值调用和无返回值的调用。
3.1 无返回值任务的调用
如果是无返回值任务的调用,可以用 execute 或者 submit 方法,这种情况下二者本质上一样。为了于有返回值任务调用保持统一,建议采用 submit 方法。
//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
//提交一个无返回值的任务(实现了Runnable接口)
executorService.submit(()->System.out.println("Hello"));
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
如果有一个任务集合,可以一个个提交。
//创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
List<Runnable> tasks = Arrays.asList(
()->System.out.println("Hello"),
()->System.out.println("World"));
//逐个提交任务
tasks.forEach(executorService::submit);
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
3.2 有返回值任务的调用
有返回值的任务需要实现 Callable 接口,实现的时候在泛型位置指定返回值类型。在调用 submit 方法时会返回一个 Future 对象,通过 Future 的方法 get () 可以拿到返回值。这里需要注意的是,调用 get () 时代码会阻塞,直到任务完成,有返回值。
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<String> future = executorService.submit(()->"Hello");
System.out.println(future.isDone());//false
String value = future.get();
System.out.println(future.isDone());//true
System.out.println(value);//Hello
如果要提交一批任务,ExecutorService 除了可以逐个提交之外,还可以调用 invokeAll 一次性提交,invokeAll 的内部实现其实就是用一个循环逐个提交任务。invokeAll 返回的值是一个 Future List。
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Callable<String>> tasks = Arrays.asList(()->"Hello", ()->"World");
List<Future<String>> futures = executorService.invokeAll(tasks);
invokeAny 方法也很有用,线程池执行若干个实现了 Callable 的任务,然后返回最先执行结束的任务的值,其它未完成的任务将被正常取消掉不会有异常。如下代码不会输出 “Hello”
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Callable<String>> tasks = Arrays.asList(
() -> {
Thread.sleep(500L);
System.out.println("Hello");
return "Hello";
}, () -> {
System.out.println("World");
return "World";
});
String s = executorService.invokeAny(tasks);
System.out.println(s);//World
输出:
World
World
另外,在查看 ExecutorService 源码时发现它还提供了一个方法 <T> Future<T> submit(Runnable task, T result); ,可以通过这个方法提交一个实现了 Runnable 接口的任务,然后有返回值,而 Runnable 接口中的 run 方法时没有返回值的。那它的返回值是哪来的呢?其实问题在于该 submit 方法后面的一个参数,这个参数值就是返回的值。调用 submit 方法之后,有一通操作,然后直接把 result 参数返回了。
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(() -> System.out.println("Hello"), "World");
System.out.println(future.get());//输出:World
4 小结
在利用多线程处理任务时,应该根据情况选择合适的任务类型和线程池类型。如果无返回值,可以采用实现 Runnable 或 Callable 接口的任务;如果有返回值,应该使用实现 Callable 接口的任务,返回值通过 Future 的 get 方法取到。选用线程池时,如果只用 1 个线程,用单线程池或者容量为 1 的固定容量线程池;处理大量 short-live 任务是,使用可缓存的线程池;若要有计划或者循环执行某些任务,可以采用计划任务线程池;如果任务需要消耗大量的 CPU 资源,应用工作窃取线程池。
Java 多线程 -- 创建和使用线程池
使用线程池的目的:
-
线程是稀缺资源,不能频繁的创建。
-
解耦作用;线程的创建于执行完全分开,方便维护。
-
应当将其放入一个池子中,可以给其他任务进行复用。
创建线程池的方式:
在 JDK 1.5 之后推出了相关的 api,常见的创建线程池方式有以下三种:
-
Executors.newCachedThreadPool()
:无限线程池。 -
Executors.newFixedThreadPool(nThreads)
:创建固定大小的线程池。 -
Executors.newSingleThreadExecutor()
:创建单个线程的线程池。
但是在阿里巴巴开发规约中对线程池的创建有以下几个强制要求:
可以看到规约是不允许使用 Executors 直接创建线程池的,而要通过 ThreadPoolExecutor 的方式创建
1 public static ExecutorService newCachedThreadPool() {
2 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
3 60L, TimeUnit.SECONDS,
4 new SynchronousQueue<Runnable>());
5 }
我们看到这三种方式的源码也是通过 ThreadPoolExecutor 的方式来创建的,接下来我们看一下 ThreadPoolExecutor 的 API
1 ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
这几个核心参数的作用:
-
corePoolSize
为线程池的基本大小。 -
maximumPoolSize
为线程池最大线程大小。 -
keepAliveTime
和unit
则是线程空闲后的存活时间。 -
workQueue
用于存放任务的阻塞队列。 -
handler
当队列和最大线程池都满了之后的饱和策略。
线程池大小创建的原则:
-
IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2
-
CPU 密集型任务(大量复杂的运算)应当分配较少的线程,比如 CPU 个数相当的大小。
通过 SpringBoot 管理线程池:
1 @Configuration
2 public class TreadPoolConfig {
3
4
5 /**
6 * 消费队列线程
7 * @return
8 */
9 @Bean(value = "consumerQueueThreadPool")
10 public ExecutorService buildConsumerQueueThreadPool(){
11 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
12 .setNameFormat("consumer-queue-thread-%d").build();
13
14 ExecutorService pool = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
15 new ArrayBlockingQueue<Runnable>(5),namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());
16
17 return pool ;
18 }
19
20
21
22 }
使用:
1 @Resource(name = "consumerQueueThreadPool")
2 private ExecutorService consumerQueueThreadPool;
3
4
5 @Override
6 public void execute() {
7
8 //消费队列
9 for (int i = 0; i < 5; i++) {
10 consumerQueueThreadPool.execute(new ConsumerQueueThread());
11 }
12
13 }
其实也挺简单,就是创建了一个线程池的 bean,在使用时直接从 Spring 中取出即可。
我们今天的关于JAVA:不使用线程怎么办?和java线程不执行的分享就到这里,谢谢您的阅读,如果想了解更多关于java notify 只唤醒一个,其他未唤醒的线程怎么办?一直阻塞着吗?、Java 使用线程发出数据库请求、Java 使用线程池执行若干任务、Java 多线程 -- 创建和使用线程池的相关信息,可以在本站进行搜索。
本文标签: