创建线程的第一种方式:继承Thread ,由子类复写run方法。
步骤:
1,定义类继承Thread类;
2,复写Thread类中的run方法,目的是:将自定义代码存储到run方法中,让线程运行;
3,通过创建Thread类的子类对象,创建线程对象;
4,调用线程的start方法,开启线程,并执行run方法。
代码:class Ticket extends Thread {//继承Thread类
private static int tick = 100;
public void run() {
while (true) {
if (tick > 0) {
System.out.println(Thread.currentThread().getName()+ "...sale : " + tick--);
}
}
}
}
class TicketDemo {
public static void main(String[] args) {
//创建四个线程
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
//启动线程并执行run方法
t1.start();
t2.start();
t3.start();
t4.start();
}
}
创建线程的第二种方式:实现一个接口Runnable
步骤:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法(用于封装线程要运行的代码)。
3,通过Thread类创建线程对象;
4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。
5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。
Ticket t = new Ticket();
/*
直接创建Ticket对象,并不是创建线程对象。
因为创建对象只能通过newThread类,或者new Thread类的子类才可以。
所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。
*/
Thread t1 = newThread(t); //创建线程。
/*
只要将t作为Thread类的构造函数的实际参数传入即可完成线程对象和t之间的关联
为什么要将t传给Thread类的构造函数呢?其实就是为了明确线程要运行的代码run方法。
*/
t1.start();
为什么要有Runnable接口的出现?
1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。
可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?
只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。
所以,通常创建线程都用第二种方式。
因为实现Runnable接口可以避免单继承的局限性。
2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。
所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。
实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。
代码:
class Ticket implements Runnable {//实现Runnable接口
private static int tick = 100;
public void run() {
while (true) {
if (tick > 0) {
System.out.println(Thread.currentThread().getName()+ "...sale : " + tick--);
}
}
}
}
class TicketDemo {
public static void main(String[] args) {
//创建一个对象
Ticket t = new Ticket();
//创建四个线程
Thread t1 = new Thread();
Thread t2 = new Thread();
Thread t3 = new Thread();
Thread t4 = new Thread();
}
}