1、继承Thread类,重写run()方法
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
/**
* 1、请编写一个程序,开启一个线程,该线程每隔一秒,在控制台输出“ hello,world!”
*/
//创建一个Cat对象,当做线程使用
Cat cat = new Cat();
cat.start();
//说明:当main线程启动一个子线程时,也就是Thread-0子线程,主线程不会阻塞,会继续执行下面的方法
for (int i = 0; i < 10; i++) {
System.out.println("主线程:" + Thread.currentThread().getName() + " 正在打印: 我是主线程");
Thread.sleep(1000);
}
}
}
//继承 Thread 类
//1、当一个类继承了Thread,该类就可以当做线程使用
//2、一般我们会重写run方法,写上自己的逻辑
//3、run Thread类 实现了 Runnable 接口的run方法
class Cat extends Thread{
int time = 0;
//重写run方法
@Override
public void run() {
while (time < 10){
//线程名称
System.out.println("线程名称:=====>"+Thread.currentThread().getName() + " 正在打印:====> hello world!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
time++;
}
}
}
2、实现Runable接口方式
1.创建一个实现了Runable接口的类
2.实现类去实现Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()
ublic class ThreadDemo01 {
public static void main(String[] args){
window1 w = new window1();
//虽然有三个线程,但是只有一个窗口类实现的Runnable方法,
//由于三个线程共用一个window对象,所以自动共用100张票
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class window1 implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(true){
if(ticket>0){
// try {
// sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName()+"当前售出第"+ticket+"张票");
ticket--;
}else{
break;
}
}
}
}
比较创建线程的两种方式:
开发中,优先选择实现Runable接口的方式
原因:
1:实现的方式没有类的单继承性的局限性
2:实现的方式更适合用来处理多个线程有共享数据的情况
联系:Thread也是实现自Runable,两种方式都需要重写run()方法,将线程要执行的逻辑声明在run中
3.新增的两种创建多线程方式
3.1 实现callable接口方式
与使用runnable方式相比,callable功能更强大些:
- runnable重写的run方法不如callaalbe的call方法强大,call方法可以有返回值
- 方法可以抛出异常
- 支持泛型的返回值
- 需要借助FutureTask类,比如获取返回结果
object ThreadTest {
def main(args: Array[String]): Unit = {
val numCallable = new NumCallable
val futureTask = new FutureTask[Int](numCallable)
val t1 = new Thread(futureTask)
t1.setName("线程1")
t1.start()
try {
//get返回值即为FutureTask构造器参数callable实现类重写的call的返回值
val sum = futureTask.get()
println(Thread.currentThread().getName() + ":" + sum);
} catch {
case e1: ExecutionException => {
e1.printStackTrace()
}
case e2: InterruptedException => {
e2.printStackTrace()
}
case e: Throwable => {
e.printStackTrace()
}
}
}
}
class NumCallable extends Callable[Int] {
private var sum: Int = 0
override def call(): Int = {
for (i <- 0 to 100 if i % 2 == 0) {
println(Thread.currentThread().getName() + ":" + i)
sum += i
}
sum
}
}
3.2 使用线程池的方式
后续会在专门的线程池专题讲解
3.3 Runnable接口和Callable接口的区别
- Runnable接口中的唯一抽象方法run()方法没有返回值,Callable接口中的唯一抽象方法call()方法有返回值;
- Runnable接口的run()方法没有受检异常的异常声明,即异常只能在内部捕获,不能继续上抛, Callable接口的call()方法声明了受检异常,可直接抛出Exception异常,并且可以不予捕获;
- Callable实例不能和Runnable实例一样,直接作为Thread线程实例的target来使用;
- 异步执行任务在大多数情况下是通过线程池去提交的,而Runnable接口和Callable接口都可以应用于线程池;
- Callable接口支持返回执行结果,此时需要调用
FutureTask.get()
方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
注意:异步执行任务在大多数情况下是通过线程池去提交的,而很少通过创建一个新的线程去提交(即通过Thread类的方式执行start()方法)。