创建线程
JAVA中线程被封装成Thread对象。JDK API中有对Thread的说明,连创建方式都有。 自定义线程有两种方式:
- 继承Thread
- 实现Runable接口
从打印结果是否是不同线程运行来验证多线程执行。 主线程代码在main方法中,自定义线程方法代码在run方法中。
两种创建方式的区别:
- Thread 的代码是存在子类当中;
- Runable方式的代码是的实现接口的子类当中,还避免了单继承的问题。
一、继承 Thread 方式
这种方式最简单,需要三个步骤:
- 继承 Thread 类
- 重写 run 方法,run方法要看成一个入口点。
- 调用 start 方法来执行自定义线程
实现代码:
代码语言:javascript复制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
是由用户开启的。
注意事项
- 注意this
如果继承自Thread类,可以直接使用this关键字来调用Thread类中的方法,如getName方法,因为是继承所以可以直接使用方法。
- 注意super
继承Thread后,也可以直接调用父类的方法给自己用,而不需要.currentThread.方法。 如super(name); 直接调用构造方法重命名线程。
二、Runable 方式
这种方式业务类必须实现Runnable
接口,并将业务类所创建的对象传入 Thread
对角中去。
由于对象只有一份,所以多个 Thread
对象操作的是同一个对象,所以就会产生共享数据的问题,即,不安全问题的产生。
代码实现
代码语言:javascript复制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方法");
}
}
多写一个例子:
代码语言:javascript复制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 一次,就是创建的一个新对象,每个对象都是一分独立的副本,并不是同一个对象。
代码语言:javascript复制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 方式的区别 这种非常重要的概念,一定要明白。关系到线程的执行方式。
- 继承Thread类方式:线程到码存放在Thread子类的run方法中。即继承了Thread类的自定义线程类的run方法中。
- 实现Runnable接口方式:线程代码存在接口的子类的run方法中。注意是接口的子类的run方法中,不是实现类的run方法中。
第2种方式最常用。 优点:
- 避免单继承的局限性。
- 多个业务代码可以有不同的代码存放区。