GVKun编程网logo

JAVA:不使用线程怎么办?(java线程不执行)

14

在本文中,我们将为您详细介绍JAVA:不使用线程怎么办?的相关知识,并且为您解答关于java线程不执行的疑问,此外,我们还会提供一些关于javanotify只唤醒一个,其他未唤醒的线程怎么办?一直阻塞

在本文中,我们将为您详细介绍JAVA:不使用线程怎么办?的相关知识,并且为您解答关于java线程不执行的疑问,此外,我们还会提供一些关于java notify 只唤醒一个,其他未唤醒的线程怎么办?一直阻塞着吗?、Java 使用线程发出数据库请求、Java 使用线程池执行若干任务、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 notify 只唤醒一个,其他未唤醒的线程怎么办?一直阻塞着吗?

Java 使用线程发出数据库请求

Java 使用线程发出数据库请求

如何解决Java 使用线程发出数据库请求?

线程和JavaFX有两个基本规则:

  1. 任何修改或访问场景图一部分的节点状态的代码都必须在JavaFX应用程序线程上执行。某些其他操作(例如,创建new Stage)也受此规则约束。
  2. 任何可能花费很长时间运行的代码都应在后台线程上执行(即不在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(...)等,这定义了一些可观察到的性质(例如statevalue):听众对这些属性将被通知的FX应用程序线程的变化。最后,有一些方便的方法来注册处理程序(setonSucceeded(...),setonFailed(...)等); 通过这些方法注册的所有处理程序也将在FX Application Thread上调用。

因此,从数据库检索数据的一般公式为:

  1. 创建一个Task来处理对数据库的调用。
  2. 使用Task执行数据库调用所需的任何状态初始化。
  3. 实现任务的call()方法以执行数据库调用,并返回调用结果。
  4. 向任务注册一个处理程序,以在完成后将结果发送到UI。
  5. 在后台线程上调用任务。 对于数据库访问,我强烈建议将实际的数据库代码封装在一个对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 使用线程池执行若干任务

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 多线程 -- 创建和使用线程池

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 多线程 -- 创建和使用线程池的相关信息,可以在本站进行搜索。

本文标签: