Java多线程实现的方式

By AverageJoeWang
 标签:

什么是线程?

进程是资源分配的最小单位,由CPU分配内存,而线程则是资源调度与执行的单位,一个进程可拥有多条线程,并且共享进程的资源,同一进程内的线程都处于同一内存空间,CPU不会在额外为其分配空间。一个进程允许并发地运行多个线程。

多线程在调用start()方法以后并不是立刻执行,而是进入到就绪状态,待时间片轮询分配资源以后才可以执行多线程。,当执行一段时间以后,需要让出资源,随后准备就绪,然后等待资源继续执行执行直到执行结束才退出。

Java实现多线程的三种方法

Java实现多线程主要有三种方式继承Thread类,实现Runnable接口,实现Callable接口。前两种方式线程执行完没有返回值,而第三种方式有返回值。

1.继承Thread类实现多线程

直接继承Thread类并重写其中run()方法实现多线程,是实现多线程最简单常用的一种方式。实质上Thread类也实现了Runnable接口。在实现线程时只需要创建子类对象,调用start()方法即可,记住不是调用run()方法,调用run()方法只算是普通的调用类内部方法而不算是实现线程,只有调用start()方法,才会生成新线程,并将新线程加入线程组中等待CPU调用。

简单示例:

class MyThread extends Thread{

    private String str;
    public MyThread(String str){
        this.str = str;
    }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++){
            System.out.println(str + "线程: " + i);
        }
    }
}

public class Threadextends {
    public static void main(String[] args){
        MyThread myThread = new MyThread("a");
        MyThread myThread1 = new MyThread("b");
        MyThread myThread2 = new MyThread("c");
        myThread.start();
        myThread1.start();
        myThread2.start();
    }
}
  • 结果
c线程: 0
c线程: 1
c线程: 2
c线程: 3
c线程: 4
b线程: 0
b线程: 1
a线程: 0
b线程: 2
b线程: 3
b线程: 4
a线程: 1
a线程: 2
a线程: 3
a线程: 4

使用这种方式创建多线程的缺点是:类必须继承Thread类,而Java为单继承,从而不能再继承其他的类了,如果一个类已经继承了其他类(如小程序必须继承Applet类),就无法继承Thread类了。

查看源码


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();
  • 继承Thread的类实现的线程由线程实例调用start()方法启动,run()方法是被复写,用来指明多线程的任务
  • start()方法在重复启动的时候会抛出异常IllegalThreadStateException,然而在调用这个方法的时候并没有使用try…catch来处理,所以IllegalThreadStateExceptionRuntimeException的子类.
  • start()方法里有一个start0()方法,而这个方法是private native void start0();,即是native类型的,是调用本机原生系统的方法

2.实现Runnable接口实现多线程

实现Runnable接口实现多线程打破了单继承的局限性。只需通过实现Runnable接口并重写其中的run()方法即可。

  • 实现Runnable接口:
public class RunnableTest implements Runnable{
    private String str;
    public RunnableTest(String str){
        this.str = str;
    }
    public void run(){
        for (int i = 0; i < 5; i++){
            System.out.println(str + "线程:" + i);
        }
    }

    public static void main(String[] args){
        RunnableTest runnableTest1 = new RunnableTest("a");
        RunnableTest runnableTest2 = new RunnableTest("b");
        RunnableTest runnableTest3 = new RunnableTest("c");
        new Thread(runnableTest1).start();
        new Thread(runnableTest2).start();
        new Thread(runnableTest3).start();
    }
}
  • 结果
a线程:0
a线程:1
a线程:2
a线程:3
a线程:4
c线程:0
b线程:0
b线程:1
b线程:2
b线程:3
b线程:4
c线程:1
c线程:2
c线程:3
c线程:4
  • 实现Runnable接口以后没有了start()方法,所以在启动的时候需要new Thread(runnableTest1).start();
  • 推荐使用实现Runnable接口实现多线程,因为:
    • 1.避免了单继承的局限性。
    • 2.便于多线程间共享资源。
  • 匿名内部类实现Runnable接口
public static void main(String[] args){
    new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++){
                    System.out.println(i);
                }
            }
        }).start();
}
  • 使用Lamdba实现Runnable接口
 new Thread(() -> System.out.println(10)).start();

3.使用ExecutorService、Callable、Future实现有返回结果的多线程

返回结果的线程是在JDK1.5中引入的新特征,加入了java.util.concurrent, ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类。想要详细了解Executor框架的可以访问这里,这里面对该框架做了很详细的解释。这种方式实现线程首先的实现Callable接口,并重写其中的call()方法,再借助执行调度服务ExecutorService获取Future对象,通过Future对象实例调用get()就可以获取到Callable任务返回的Object了.

  • 简单示例:
    创建Callable实现类,重写call()方法:
class MyNewThread implements Callable<String>{

    private int ticket = 10;
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 20; i++){
            if (ticket > 0){
                System.out.println("ticket:" + this.ticket--);
            }
        }
        return "ticket done";
    }
}
  • 创建并调用线程:
public class CallableTest{
    public static void main(String[] args) throws Exception{
        FutureTask<String> task = new FutureTask<String>(new MyNewThread());
        new Thread(task).start();
        System.out.println(task.get());
    }
}
  • 结果
ticket:10
ticket:9
ticket:8
ticket:7
ticket:6
ticket:5
ticket:4
ticket:3
ticket:2
ticket:1
ticket done

如此就实现了多线程,通过这种方法实现多线程有返回值,并且可以向外抛出异常,但是代码相对繁琐。

Thread,callable和Runnable区别

  • 多线程实现上使用代理设计模式
  • ThreadRunnable接口的子类
  • RunnableThread没有返回值,Callable有返回值