Java中的多线程

一、Java中线程实现

Java 中实现多线程的代码有三种方式,一种是继承 Thread 类,另一种是实现 Runnable 接口,在JDK1.5之后还有一个 Callable 接口,Runnable 接口方式利于资源共享的处理,Callable 接口的实现方式可以获取线程的返回值。

1. 方法1——继承 Thread 类

Thread 类是在 java.lang 包中定义的。一个类只要继承了 Thread 类就称为多线程操作类。在 Thread 的子类中必须明确覆写 Thread 类中的 run() 方法,此方法为线程主体。线程类定义如下:

class 类名 extends Thread {
    属性...
    方法...
    public void run() {
        线程主体
    }
}

启动线程是调用 Thread 类的 start() 方法,而不是 run() 方法。若直接调用 run() 方法就是一个普通方法调用,而不是多线程。并且 start() 方法只能调用一次,因为 start() 方法中有一个调用计数,多次调用会 throw new IllegalThreadStateException() 异常。

例子:

class MyThread extends Thread {
    private String name;
    public MyThread(String name) {
        this.name = name;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("name: " + name + " i=" + i);
        }
    }
}

public class ThreadDemo {
    public static void main(String args[]) {
        MyThread mt1 = new MyThread("mt1");
        MyThread mt2 = new MyThread("mt2");
        //mt1.run(); //简单的方法调用
        //mt2.run();
        mt1.start();
        mt2.start();
        //mt2.start(); //触发IllegalThreadStateException异常
    }
}

 

2. 方法2——实现 Runnable 接口

Java 中也可以通过实现 Runnable 接口的方式实现多线程,此接口定义为:

public interface Runnable {
    public void run();
}

使用 Runnable 接口实现多线程的格式:

class 类名 implements Runnable {
    属性...
    方法...
    public void run() {
        线程主体
    }
}

Runnable 接口实际上还是依靠 Thread 实现多线程启动的,可以看 Thread 类的定义就知道使用方法了:

public class Thread extends Object implements Runnable {
    private Runnable target;
    
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
    private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
        ...
        this.target = target;
        ...
    }
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

如果传了 Runnable 类型的参数,最终执行的就是 Runnable 参数的 run() 方法。

举例1:

class MyThread implements Runnable {
    private String name;
    public MyThread(String name) {
        this.name = name;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("name: " + name + " i=" + i);
        }
    }
}

public class ThreadDemo {
    public static void main(String args[]) {
        MyThread mt1 = new MyThread("mt1");
        MyThread mt2 = new MyThread("mt2");
        Thread t1 = new Thread(mt1); //传腹泻run()方法的Runnable的子类
        Thread t2 = new Thread(mt2);
        t1.start();
        t2.start();
    }
}

通过 Runnable 接口实现多线程比起通过实现 Thread 类实现多线程的优势是便于多个同类对象资源共享时的处理。因为后者的运行的 run() 方法是来自参数对象的,因此多个线程传同一个参数对象的话其属性就只有一份资源。而前者需要定义多个对象然后调用其 run() 方法实现多线程,由于是多个对象,其属性就是多个资源了。开发过程中建议使用 Runnable 接口的实现方式实现多线程。


3. 方法3——利用 Callable 接口

通过 Runnable 接口实现的多线程会出现 run() 方法不能返回操作结果的问题,为了解决此问题,JDK1.5开始提供了一个新的接口 java.util.concurrent.Callable,定义如下:

public interface Callable<V> {
    public V call() throws Exception;
}

call() 方法在执行完后可以返回一个具体类型的数据。但是 Thread 类中没有定义任何构造方法来接收 Callable 接口对象实现对象,这导致多线程的启动又遇到了问题,JDK1.5之后开始提供一个 java.util.concurrent.FutureTask<V> 类来解决这个问题,其定义:

public class FutureTask<V> extends Object implements RunnableFuture<V>

FutureTask 实现了 RunnableFuture 接口,而后者又同时实现了 Future 和 Runnable 接口。如果想要接收线程执行的返回结果,调用 Future 接口中的 get() 方法即可。FutureTask 类常用方法如下:

public FutureTask(Callable<V> callable); //构造函数,接收 Callable 接口对象实例
public FutureTask(Runnable runnable, V result); //接收 Runnable 接口实例,并指定返回结果类型
public V get() throws InterruptedException, ExecutionException; //取得线程的执行结果,由 Future 接口定义

FutureTask 是 Runnable 接口的子类,并且其构造函数可以接收 Callable 实例,因此依然可以利用 Thread 类来实现多线程的启动。若想获取线程执行结果,则利用 Future 接口中的 get() 方法。

例子:

import java.util.concurrent.Callable;

class MyThread implements Callable<String> {
    private int ticket = 5;

    @override
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            if (ticket > 0) {
                System.out.println("ticket left: " + ticket--);
            }
        }
        return "sold out";
    }
}

public class ThreadDemo {
    public static void main(String args[]) {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();

        FutureTask<String> task1 = new FutureTask<String>(mt1);
        FutureTask<String> task2 = new FutureTask<String>(mt2);

        new Thread(task1).start();
        new Thread(task2).start();

        System.out.println("task1 return: " + task1.get());
        System.out.println("task2 return: " + task2.get());
    }
}

但是报找不到 FutureTask 这个符号,可能是我本地 java 版本比较低。

Runnable 接口是 Java 最早提供也是使用最广泛的,平时建议通过使用 Runnable 接口的方式实现多线程。

 

上一篇:Java线程的创建和同步


下一篇:java多线程学习笔记三——线程创建(实现Runnable接口)