在java中可有两种方式实现多线程:
1. 继承Thread类2. 是实现Runnable接口由于Java不支持多继承,所以推荐使用实现Runnable接口的方法。
如果想要新建一个线程类,那么只需要override run() method就好了。
实现Runnable的类在创建新线程的时候用 Tread tread = new Tread(实现Runnable接口的类),就完成了Tread的创建。
- State transitions of a thread
Thread类的两个主要method: start() 和 run()的区别
start()
- 让thread进入 runnable 状态 - call run() method - extend Thread Class不用override start() methodrun()
- 当thread运行时被调用 - run()在thread创建完了之后被调用 - Runnable类里的run()没有任何内容,所以一定要实现
- GUI和Threads
在Java中,GUI的绘制和事件的处理都是集中在一个线程之中完成的,这个线程叫做Event Dispatcher Thread - 事件分发线程 简称EDT
我在CSDN上发现了一个关于Swing EDT的很好的文章
"EDT:Swing程序只有一个EDT,该线程负责GUI组件的绘制和更新,通过调用程序的事件处理器来响应用户交互。所有事件处理都是在EDT上进行的,程序同UI组件和其基本数据模型的交互只允许在EDT上进行,所有运行在EDT上的任务应该尽快完成,以便UI能及时响应用户输入。" 简单来说EDT机制是为了防止GUI的绘制和事件互相影响(interrupted)。为什么要在这里谈到EDT呢?
因为下面一个概念不是很好理解(至少javadoc写得很模糊): Platform.runLater(): 如果你有多个线程,其中包括一个GUI线程(也就是EDT),一个是没有GUI的线程。比如说你需要完成一个进度条,但是如何才能让进度(非GUI线程)和进度条(GUI线程)相互关联呢?那么Platform.runlater()就很有用了。
Platform.runlater()的具体实现也有主要两种方法,比如说我们想要创建一个从1数到1,000,000的进度条:
第一种写法是这样的:
Code using PlatForm.RunLater :
final ProgressBar bar = new ProgressBar();new Thread(new Runnable() { @Override public void run() { for (int i=1; i<=1000000; i++) { final int counter = i; Platform.runLater(new Runnable() { @Override public void run() { bar.setProgress(counter/1000000.0); } }); } }}).start();
第一种写法其实是一种很反人类的写法,你的脑子会因为两个Runnable多死几个细胞!
第二种实现是:
Code using Task :
Task task = new Task() { @Override public Void run() { static final int max = 1000000; for (int i=1; i<=max; i++) { updateProgress(i, max); } return null; }};ProgressBar bar = new ProgressBar();bar.progressProperty().bind(task.progressProperty());new Thread(task).start();
这样的写法就简洁很多。
具体的讨论详见stackoverflow:
但不是说我们不能用Platform.runlater(),下面是一个使用Platform.runlater()实现进度条的例子。
import java.util.concurrent.locks.ReentrantLock;import javafx.application.Application;import static javafx.application.Application.launch;import javafx.application.Platform;import javafx.concurrent.Task;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.ProgressBar;import javafx.scene.control.ProgressIndicator;import javafx.scene.layout.HBox;import javafx.scene.layout.VBox;import javafx.scene.text.Font;import javafx.stage.Stage;public class BetterProgressTest extends Application { ProgressBar bar; ProgressIndicator indicator; Button button; Label processLabel; int numTasks = 0; ReentrantLock progressLock; @Override public void start(Stage primaryStage) throws Exception { progressLock = new ReentrantLock(); VBox box = new VBox(); HBox toolbar = new HBox(); bar = new ProgressBar(0); indicator = new ProgressIndicator(0); toolbar.getChildren().add(bar); toolbar.getChildren().add(indicator); button = new Button("Restart"); processLabel = new Label(); processLabel.setFont(new Font("Serif", 36)); box.getChildren().add(toolbar); box.getChildren().add(button); box.getChildren().add(processLabel); Scene scene = new Scene(box); primaryStage.setScene(scene); button.setOnAction(e -> { Tasktask = new Task () { int task = numTasks++; double max = 200; double perc; @Override protected Void call() throws Exception { try { progressLock.lock(); for (int i = 0; i < 200; i++) { System.out.println(i); perc = i/max; // THIS WILL BE DONE ASYNCHRONOUSLY VIA MULTITHREADING Platform.runLater(new Runnable() { @Override public void run() { bar.setProgress(perc); indicator.setProgress(perc); processLabel.setText("Task #" + task); } }); // SLEEP EACH FRAME //try { Thread.sleep(10); // } catch (InterruptedException ie) { // ie.printStackTrace(); // } }} finally { progressLock.unlock(); } return null; } }; // THIS GETS THE THREAD ROLLING Thread thread = new Thread(task); thread.start(); }); primaryStage.show(); } public static void main(String[] args) { launch(args); }}
例子里面用到了Task类,关于Java并发的内容并不是很熟悉,就不深入研究了。
如何结果(kill)掉一个线程?
常见的做法是设置一些条件(loop, exception)来让thread在运行达到一定结果之后终止(自我毁灭)。
- Timer和TimerTask 用来创建和时间相关的类
- Need program to do something X times/second
- TimerTask可以看做第三种实现线程的方式(extend threads 和 implement Runnable之外) "Timer和TimerTask可以做为实现线程的第三种方式,前两中方式分别是继承自Thread类和实现Runnable接口。 Timer是一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行,可以看成一个定时器,可以调度TimerTask。TimerTask是一个抽象类,实现了Runnable接口,所以具备了多线程的能力。"