创建线程

JAVA中线程被封装成Thread对象。JDK API中有对Thread的说明,连创建方式都有。
自定义线程有两种方式:

  1. 继承Thread
  2. 实现Runable接口

从打印结果是否是不同线程运行来验证多线程执行。
主线程代码在main方法中,自定义线程方法代码在run方法中。

两种创建方式的区别:

  1. Thread 的代码是存在子类当中;
  2. Runable方式的代码是的实现接口的子类当中,还避免了单继承的问题。

一、继承 Thread 方式

这种方式最简单,需要三个步骤:

  1. 继承 Thread 类
  2. 重写 run 方法,run方法要看成一个入口点。
  3. 调用 start 方法来执行自定义线程

实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestThread extends Thread {
//1.继承Thread
@Override
public void run() {

//2.重写run方法
//super.run();
for (int i = 0; i < 100; i++) {
System.out.println("run: "+ i);
}
}

public static void main(String[] args) {
//3.执行start线程
TestThread testThread = new TestThread();
testThread.start();
for (int i = 0; i < 100; i++) {
System.out.println("main: "+ i);
}
}
}

结果
会交替打印 run 和 main 证明CPU执行线程时是交替进行的。而且这种交替是随机性的。

说明:run方法中写上自定义线程要执行的程序,而调用 start 才是真正表示开始执行这条自定义线程。
理解: main是程序默认线程入口,run 是自定义程序入口,和 main 等价。
但是 main 是程序开启的,run 是由用户开启的。

注意事项

  1. 注意this

如果继承自Thread类,可以直接使用this关键字来调用Thread类中的方法,如getName方法,因为是继承所以可以直接使用方法。

  1. 注意super

继承Thread后,也可以直接调用父类的方法给自己用,而不需要.currentThread.方法。
如super(name); 直接调用构造方法重命名线程。

二、Runable 方式

这种方式业务类必须实现Runnable接口,并将业务类所创建的对象传入 Thread 对角中去。
由于对象只有一份,所以多个 Thread 对象操作的是同一个对象,所以就会产生共享数据的问题,即,不安全问题的产生。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestSun {
public static void main(String[] args) {
TestClass c = new TestClass();
TestClass d = new TestClass();

Thread t1 = new Thread(c); //TestClass 必须实现Runnable的run方法才能使用这种方式。
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}

// 继承 Runnable
class TestClass implements Runnable {
public void run() {
System.out.println("子类重写run方法");
}
}

多写一个例子:

1
2
3
4
5
6
7
8
9
10
11
public class TestSun implements Runnable {
public static void main(String[] args) {
TestSun testSun = new TestSun();
Thread t = new Thread(testSun);
t.start();
}

public void run() {
//do
}
}

理解:例用实现Runnable还有一个好处就是,我不同的类,我可以有不同的run实现方法,

Runable 共享资源问题

下面代码的执行结果是:输出 50次。如果使用继承Thread的方式的话,会被执行200次。
原就是就因为 Test 对象实际上被所有线程所共享,所有线程所操作的时同一个对象。
如果使用Thread方式,给变量i设为静态也可以做到执行50次,但是静态的生命周期太长了,不推荐。
这个例子为了说明 Thread 和 Runable 的区别。
而为什么 Thread 对执行多次,是因为继承的Thread 后,每new 一次,就是创建的一个新对象,每个对象都是一分独立的副本,并不是同一个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.liukai.thread;

public class TestThread2 {
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
jhread t3 = new Thread(test);
t1.start();
t2.start();
t3.start();
}

}

class Test implements Runnable {
boolean flag = true;
int i = 50;

public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + "---" + i--);
if (i <= 0) {
flag = false;
}
}

}

}

区别

runnable 和 继承Thread 方式的区别
这种非常重要的概念,一定要明白。关系到线程的执行方式。

  1. 继承Thread类方式:线程到码存放在Thread子类的run方法中。即继承了Thread类的自定义线程类的run方法中。
  2. 实现Runnable接口方式:线程代码存在接口的子类的run方法中。注意是接口的子类的run方法中,不是实现类的run方法中。

第2种方式最常用。
优点:

  1. 避免单继承的局限性。
  2. 多个业务代码可以有不同的代码存放区。