# Java实现线程的三种方式

Java实现线程的三种方式：

1. 继承Thread
2. 实现Runnable接口
3. 实现Callable接口

## 1. 区别：

1. 第一种方式继承Thread就不能继承其他类了，后面两种可以；
2. 使用后两种方式可以多个线程共享一个target；
3. Callable比Runnable多一个返回值，并且call()方法可以抛出异常；
4. 访问线程名，第一种直接使用this.getName()，后两种使用Thread.currentThread().getName()。

下面我们通过代码来看一下实现和区别：

## 2. 三种实现：

```java
//1. 继承Thread，重写run()方法
class Thread1 extends Thread {

    private int n = 5;

    @Override
    public void run() {
        while(n > 0) {
            System.out.println("name:" + this.getName() + ", n:" + n);
            n--;
        }
    }
}
//2. 实现Runnable接口，实现run()方法
class Thread2 implements Runnable {

    private int n = 5; 

    @Override
    public void run() {
        while(n > 0) {
            System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
            n--;
        }
    }
}
//3. 实现Callable接口，实现call()方法，带有返回值和异常
class Thread3 implements Callable<String> {

    private int n = 5;

    @Override
    public String call() throws Exception {
        while(n > 0) {
            System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
            n--;
        }
        return String.valueOf(n);
    }

}
```

如何使用：

```java
//第一种实现方式
Thread1 t11 = new Thread1();
Thread1 t12 = new Thread1();
Thread1 t13 = new Thread1();

t11.start();
t12.start();
t13.start();

//第二种实现方式
Thread2 t21 = new Thread2();
Thread2 t22 = new Thread2();
Thread2 t23 = new Thread2();

Thread t211 = new Thread(t21);
Thread t212 = new Thread(t22);
Thread t213 = new Thread(t23);

t211.start();
t212.start();
t213.start();

//第三种实现
Thread3 t31 = new Thread3();
Thread3 t32 = new Thread3();
Thread3 t33 = new Thread3();

FutureTask<String> f1 = new FutureTask<>(t31);
FutureTask<String> f2 = new FutureTask<>(t32);
FutureTask<String> f3 = new FutureTask<>(t33);

Thread t311 = new Thread(f1);
Thread t312 = new Thread(f2);
Thread t313 = new Thread(f3);

t311.start();
t312.start();
t313.start();
```

从代码可以看出以上提到的区别1，3，4。那么区别2共享一个target是什么意思呢？

首先我们看一下上述代码的运行结果，

第一种：

```java
name:Thread-1, n:5
name:Thread-1, n:4
name:Thread-1, n:3
name:Thread-1, n:2
name:Thread-1, n:1
name:Thread-2, n:5
name:Thread-2, n:4
name:Thread-2, n:3
name:Thread-2, n:2
name:Thread-2, n:1
name:Thread-0, n:5
name:Thread-0, n:4
name:Thread-0, n:3
name:Thread-0, n:2
name:Thread-0, n:1
```

第二种：

```java
name:Thread-4, n:5
name:Thread-4, n:4
name:Thread-4, n:3
name:Thread-3, n:5
name:Thread-5, n:5
name:Thread-3, n:4
name:Thread-4, n:2
name:Thread-4, n:1
name:Thread-3, n:3
name:Thread-3, n:2
name:Thread-3, n:1
name:Thread-5, n:4
name:Thread-5, n:3
name:Thread-5, n:2
name:Thread-5, n:1
```

可以看到，这两种方式的结果一样，都是new了三个线程，每个线程内部循环5次。第二种方式并没有体现共用同一个target。如果我们将第二种创建线程的方式改为：

```java
//第二种实现方式
Thread2 t21 = new Thread2();
Thread2 t22 = new Thread2();
Thread2 t23 = new Thread2();

Thread t211 = new Thread(t21);
Thread t212 = new Thread(t21);
Thread t213 = new Thread(t21);

t211.start();
t212.start();
t213.start();
```

看一下运行结果：

```java
name:Thread-4, n:5
name:Thread-4, n:4
name:Thread-4, n:3
name:Thread-4, n:2
name:Thread-4, n:1
name:Thread-3, n:5
name:Thread-5, n:5
```

可以看到，虽然也启动了3个线程，但是由于共享一个target，n的值改变了，其他两个线程也会知道，所以因此一共循环了5次。但是这里明明是7次啊，这是由于多线程的同步问题，可以给run方法加上synchronized关键字解决：

```java
@Override
public synchronized void run() {
  while(n > 0) {
    System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
    n--;
  }
}
```

运行结果：

```java
name:Thread-3, n:5
name:Thread-3, n:4
name:Thread-3, n:3
name:Thread-3, n:2
name:Thread-3, n:1
```

这里可能有点迷惑，只启动了一个线程啊。其实另外两个线程也启动了，只是这个时候n=0无法进入循环。我们可以加一行打印：

```java
@Override
public synchronized void run() {
  System.out.println("进入" + Thread.currentThread().getName() + "线程");
  while(n > 0) {
    System.out.println("name:" + Thread.currentThread().getName() + ", n:" + n);
    n--;
  }
}
```

可以看到运行结果：

```
进入Thread-3线程
name:Thread-3, n:5
name:Thread-3, n:4
name:Thread-3, n:3
name:Thread-3, n:2
name:Thread-3, n:1
进入Thread-5线程
进入Thread-4线程
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jun-wang.gitbook.io/learnjava/ji-shu-xue-xi/java-duo-xian-cheng/java-shi-xian-xian-cheng-de-san-zhong-fang-shi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
