英文原文 : Java Thread Example - Extending Thread Class and Implementing Runnable Interface
程序运行的基础单元就是 进程 和 线程。Java多线程编程主要针对线程。
进程
一个进程有着独立的运行环境,我们可以把进程看作一个程序或者一个应用。进程中包含着若干个线程。JRE会以单个进程的方式启动,但是会包含若干个Class以及线程。
线程
线程可以看作轻量级进程。创建一个线程只需要少量的资源,一个进程中的多个线程可以互相共享进程中的资源。
Java多线程
任何一个Java应用都至少拥有一个线程 -- main thread。当然也有许多运行在后台的线程,诸如:内存管理,系统管理,信号通信管理等等。但一个程序启动都是通过main thread,我们可以通过它来创建多个线程。
多线程是指两个或者两个以上线程同时运行在一个程序中。在单核CPU中,通过分时分片占用CPU的方式来达到多个线程同时运行的效果。
多线程的优点
- 相比进程,多线程更加轻量,创建一个线程比创建一个进程花费更少的资源。
- 线程可以共享父线程中的资源和字节码。
- 多个线程之间的环境转换相比进程开销更小。
- 线程之间的相互通信相比进程更加容易。
Java提供两种编程方式创建一个线程
- 实现java.lang.Runnable接口。
- 继承java.lang.Thread类。
实现Runnable接口创建线程例子
实现Runnable接口并重写其
public void run();
方法就可以创建一个线程类。若想把这个类当作一个线程运行起来,我们需要通过类对象来创建一个线程对象,然后调用start() 方法,在一个单独的线程来执行run() 方法。
下面是一个实例代码:
HeavyWorkRunnable.java
package com.journaldev.threads;
public class HeavyWorkRunnable implements Runnable {
@Override
public void run() {
System.out.println("Doing heavy processing - START "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
//Get database connection, delete unused data from DB
doDBProcessing();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Doing heavy processing - END "+Thread.currentThread().getName());
}
private void doDBProcessing() throws InterruptedException {
Thread.sleep(5000);
}
}
通过继承Thread类来创建一个线程
我们可以继承java.lang.Thread类,并重写其run() 方法。这样我们可以实例化此类,调用start() 方法来执行run方法中的代码。
例子代码: MyThread.java
package com.journaldev.threads;
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println("MyThread - START "+Thread.currentThread().getName());
try {
Thread.sleep(1000);
//Get database connection, delete unused data from DB
doDBProcessing();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("MyThread - END "+Thread.currentThread().getName());
}
private void doDBProcessing() throws InterruptedException {
Thread.sleep(5000);
}
}
下面的代码是展示如何执行:ThreadRunExample.java
package com.journaldev.threads;
public class ThreadRunExample {
public static void main(String[] args){
Thread t1 = new Thread(new HeavyWorkRunnable(), "t1");
Thread t2 = new Thread(new HeavyWorkRunnable(), "t2");
System.out.println("Starting Runnable threads");
t1.start();
t2.start();
System.out.println("Runnable Threads has been started");
Thread t3 = new MyThread("t3");
Thread t4 = new MyThread("t4");
System.out.println("Starting MyThreads");
t3.start();
t4.start();
System.out.println("MyThreads has been started");
}
}
上面的代码执行结果如下:
Starting Runnable threads
Runnable Threads has been started
Doing heavy processing - START t1
Doing heavy processing - START t2
Starting MyThreads
MyThread - START Thread-0
MyThreads has been started
MyThread - START Thread-1
Doing heavy processing - END t2
MyThread - END Thread-1
MyThread - END Thread-0
Doing heavy processing - END t1
当我们开启一个线程后,它的执行时间和片区是交于系统来决定的,我们并不可以控制其运行。但是我们可以设置一个线程的优先级遗憾的是,即使设置了线程的优先级,也并不能保证优先级高的线程就一定会被先执行。
Runnable vs Thread
如果你想使你的程序更加的灵活,那么你最好实现Runnable接口来创建一个线程类。若你只是为了你只是为了创建一个线程这唯一个目的的话,可以使用继承Thread的方法。
由于Java不支持多继承这种情况发生,所以实现Runnable接口可以让你这个类在以后的开发中,继承其他类。
正如你观察的那样,一个线程运行起来的时候, 并不会返回结果,是一个void方法,那如果我们想得到线程运行后返回结果请点击下面的链接Java Callable Future
在最新的Java8中,Runnable类是一个Functional Interface ,我们可以使用Lambda表达式去重写run() 方法而不是使用传统的匿名类的方式。