Java 实现多线程的三种方式
-
继承 Thread 类
-
实现 Runnable 接口(Callable 类)
继承 Thread 类
Thread 类是一个支持多线程的功能类,只要有一个子类它就可以实现多线程的支持
public class MyThread extends Thread {// 多线程的操作类}
所有程序的起点是 main()方法,线程的起点是 run()方法。
在多线程的每个主体类中都要覆写 Thread 类中所提供的 run()方法。
public class MyThread extends Thread(){
@Override
public void run(){
super.run();
}
}
所有线程和进程是一样的,要轮流抢占资源,所以多线程执行应该是多个线程彼此交替执行。
如果直接调用 run()方法,不能直接启动多线程。
而是要使用 start()方法。(调用此方法执行的方法体是 run()方法定义的)
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
IllegalThreadStateException 是 RuntimeException 的子类,选择性处理。
如果一个线程对象重复启动,就会抛出此异常。
start0 方法与抽象方法类似,但是使用了 native 声明。(调用操作系统的 API)
所以此操作是 JVM 根据不同的操作系统实现。
实现 Runnable 接口
推荐使用这种方法。
虽然 Thread 类可以实现多线程的主体类定义,但是 Java 具有单继承局限,所以针对类的继承都应该回避。
多线程也一样。为了解决单继承的限制,专门实现了 Runnable 接口。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
接口中的所有方法都是 public
只需要让一个类实现 Runnable 接口、覆写 run()方法,就可以实现多线程。
但是和之前的区别就是,不能直接继承 start 方法。不能通过 start 方法启动。
不论任何情况,要想启动多线程,一定要依靠 Thread 类完成
在 Thread 类中有可以接受 Runnable 接口对象的 constructor
class MyThread implements Runnable{
// 多线程的操作类
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
for (int x = 0 ;x < 200; x++){
System.out.println(this.name+"---->"+x);
}
}
}
public class Main{
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
MyThread mt3 = new MyThread("线程C");
new Thread(mt1).start();
new Thread(mt2).start();
new Thread(mt3).start();
}
}
基本工作中基本都是实现 Runnable 接口。
多线程两种实现方式的区别(面试题)
1.首先明确,使用 Runnable 比 Thread,解决了单继承的局限性。
public class Thread implements Runnable {}
Thread 类实现了 Runnable 接口。(有 start 方法,就是一个线程对象,可以直接启动)
整个结构有点像代理设计模式。但是代理的话应该也调用 run 方法,但是使用的 start 方法。
2.使用 Runnable 比 Thread 可以更好的描述数据共享的概念。
此时的数据共享指的是多个线程访问同一资源的操作。
- 如果使用 Thread:
class MyThread extends Thread{
// 多线程的操作类
private int ticket = 10;
@Override
public void run() {
for (int x = 0 ;x < 200; x++){
if (this.ticket > 0){
System.out.println("卖票 ,ticket = " + this.ticket--);
}
}
}
}
public class Main{
public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt1.start();
mt2.start();
mt3.start();
}
}
out:
卖票 ,ticket = 10
卖票 ,ticket = 9
卖票 ,ticket = 8
卖票 ,ticket = 7
卖票 ,ticket = 6
卖票 ,ticket = 5
卖票 ,ticket = 4
卖票 ,ticket = 3
卖票 ,ticket = 2
卖票 ,ticket = 1
卖票 ,ticket = 10
卖票 ,ticket = 9
卖票 ,ticket = 8
卖票 ,ticket = 7
卖票 ,ticket = 6
卖票 ,ticket = 5
卖票 ,ticket = 4
卖票 ,ticket = 3
卖票 ,ticket = 2
卖票 ,ticket = 1
卖票 ,ticket = 10
卖票 ,ticket = 9
卖票 ,ticket = 8
卖票 ,ticket = 7
卖票 ,ticket = 6
卖票 ,ticket = 5
卖票 ,ticket = 4
卖票 ,ticket = 3
卖票 ,ticket = 2
卖票 ,ticket = 1
Process finished with exit code 0
- 如果使用 Runnable
class MyThread implements Runnable{
// 多线程的操作类
private int ticket = 10;
@Override
public void run() {
for (int x = 0 ;x < 200; x++){
if (this.ticket > 0){
System.out.println("卖票 ,ticket = " + this.ticket--);
}
}
}
}
public class Main{
public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
}
}
out:
卖票 ,ticket = 10
卖票 ,ticket = 9
卖票 ,ticket = 8
卖票 ,ticket = 7
卖票 ,ticket = 6
卖票 ,ticket = 5
卖票 ,ticket = 4
卖票 ,ticket = 3
卖票 ,ticket = 2
卖票 ,ticket = 1
Process finished with exit code 0
这样就实现了数据共享
多线程的第三种实现方式
Runnable 里面的 run 方法不能返回操作结果。
为了解决这样,提供了新的 Callable 接口。
在 java.util.concurrent 包中。
@FunctionalInterface
public interface Callable<V> {
public V call() throws Exception;
}
返回结果的类型由 Callable 接口上的泛型来决定。
class MyThread implements Callable<String>{
private int ticket = 10;
@Override
public String call() throws Exception {
for (int x = 0 ; x <100 ; x++){
if(this.ticket>0){
System.out.println("卖票,ticket = "+ ticket--);
}
}
return "票已经卖光";
}
}
Thread 类中并没有 Callable 的构造方法,但是 jdk1.5 开始,有 java.util.concurrent.FutureTask
专门来负责 Callable 接口类的操作
public class FutureTask<V> implements RunnableFuture<V> {}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
在 FutureTask 中有如下构造方法
public FutureTask(Callable<V> callable) {}
接受的目的只有一个,取得 call 方法的返回结果。
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
FutureTask<String> task1 = new FutureTask<>(mt1);
FutureTask<String> task2 = new FutureTask<>(mt2);
// 是Runnable的子类,可以使用Thread来启动
new Thread(task1).start();
new Thread(task2).start();
// 通过get获取返回值
System.out.println("A返回"+task1.get());
System.out.println("B返回"+task2.get());
}
}
out:
卖票,ticket = 10
卖票,ticket = 9
卖票,ticket = 8
卖票,ticket = 7
卖票,ticket = 6
卖票,ticket = 5
卖票,ticket = 4
卖票,ticket = 10
卖票,ticket = 3
卖票,ticket = 9
卖票,ticket = 2
卖票,ticket = 8
卖票,ticket = 1
卖票,ticket = 7
卖票,ticket = 6
卖票,ticket = 5
卖票,ticket = 4
卖票,ticket = 3
A返回票已经卖光
卖票,ticket = 2
卖票,ticket = 1
B返回票已经卖光
Process finished with exit code 0