创建线程有两种方式,一种是通过继承Thread类,覆盖run方法。另外一种是通过实现Runnable接口。
1 通过继承Thread类实现多线程
其步骤如下:
(1)定义类继承Thread类。
(2)覆盖Thread类中的run方法。
(3)通过Thread类的子类对象创建线程对象。
(4)调用线程对象的start方法,开启线程。
其实线程要执行什么内容,线程自己便有定义,这个定义就是Thread中的run方法,这个方法中定义的内容就是线程要运行的内容。可是Thread中的run方法的内容并不是我们所需要的。怎么能让线程去运行我们自定义的内容呢?只有覆盖Thread类中的run方法,而覆盖之前需要先继承。这就是为什么要继承Thread类并覆盖run方法的原因。
主线程要运行的任务都在main函数中,自定义的线程要运行的任务都在run方法中。run方法就是专门用于存储线程任务代码的地方。
【例1.1】使用继承Thread类,覆盖run方法的方式创建线程。
ZiThread.java
//第一步:定义ZiThread类继承Thread类
public class ZiThread extends Thread {
private String name;
ZiThread(String name)
{
this.name = name;
}
//第二步:覆盖Thread类中的run方法
public void run(){
//将线程要运行的自定义的任务都封装到了run方法中
show();
}
public void show()
{
for(int x=1; x<=10; x++)
{
for(int y=-99999999; y<99999999; y++){}//刻意写个循环让程序运行慢一点。
//Thread.currentThread():获取当前执行线程的对象
//getName()返回该线程的名称
System.out.println(x+":"+name+"-->"+Thread.currentThread().getName());
}
}
}
ThreadDemo .java
public class ThreadDemo {
public static void main(String[] args) {
//第三步:通过Thread类的子类对象创建线程对象
ZiThread d1 = new ZiThread("旺财");//d1是线程对象
ZiThread d2 = new ZiThread("小明");//d2是线程对象
//第四步:调用线程对象的start方法,开启线程
d1.start();
d2.start();
/*
线程光创建不行,还需要运行才会出现多条执行路径。
发现要运行线程,必须使用start方法。
start方法做了两件事:1,开启了线程让线程可以运行,2,调用了run方法。
*/
}
}
运行结果:
线程对象的d1,d2调用run方法和调用start方法有什么区别呢?调用run方法,仅仅是一般对象在调用对象中的方法,并没有开启线程,还是主线程来完成的run方法的运行。调用start方法,是开启了一个线程(一个新的执行路径)这个线程去执行了run方法。
下面模拟售票系统来举例说明。某售票点一共有100张票,通过四个窗口卖票。卖票的动作被四个窗口同时(线程思想)使用,那就需要创建线程。可通过继承Thread类,覆盖run方法。
【例1.2】通过继承Thread类实现多进程实例。
TicketWindows.java
//第一步:定义TicketWindows类继承Thread类
public class TicketWindows extends Thread {
private int tickets=100;
//第二步:复写父类Thread中run()方法
public void run() {
while(true){
if(tickets>0){
String name=Thread.currentThread().getName();
System.out.println(name+"正在发售第"+tickets--+"张票");
}
}
}
}
NanJingStation.java
public class NanJingStation {
public static void main(String[] args) {
//第三步:创建四个线程对象
NanJingStation s1=new NanJingStation();
NanJingStation s2=new NanJingStation();
NanJingStation s3=new NanJingStation();
NanJingStation s4=new NanJingStation();
s1.start();
s2.start();
s3.start();
s4.start();
}
}
运行结果:
2 通过实现Runnable接口实现多线程
步骤:
(1)定义一个类,实现Runnable接口。
(2)覆盖Runnable接口中的run方法,将线程要运行的代码存储到run方法中。
(3)创建该接口的子类对象。
(4) 通过Thread类进行线程的创建,并将Runnable接口的子类对象作为Thread类的构造函数的实参进行传递。
为什么要传递呢?这是为了让线程对象创建时,就要明确要运行哪个run方法,而这个run方法是需要被线程对象调用的,所以将run方法所属的对象传递给Thread类的构造函数。
(5)调用Thread类中的start方法开启线程。
【例2.1】通过实现Runnable接口实现多进程实例
TicketWindows.java
//第一步:定义TicketWindows类实现Runnable类
public class TicketWindows implements Runnable {
private int tickets=100;
//第二步:覆盖Runnable接口中的run方法
public void run() {
while(true){
if(tickets>0){
String name=Thread.currentThread().getName();
System.out.println(name+"正在发售第"+tickets--+"张票");
}
}
}
}
NanJingStation.java
public class TicketWindows {
public static void main(String[] args) {
//第三步:创建任务对象
NanJingStation s=new NanJingStation();
/*第四步:创建线程对象(将子类NanJingStation中的对象作为实参传递,便于
知道调用的是哪个对象的run方法(这里只有一个对象t,调用的自然
是t中的run方法))
*/
Thread t1=new Thread(s,"窗口1");//线程处于新建状态
Thread t2=new Thread(s,"窗口2");
Thread t3=new Thread(s,"窗口3");
Thread t4=new Thread(s,"窗口4");
//第五步:开启线程
t1.start();//线程处于就绪状态
t2.start();
t3.start();
t4.start();
}
}
运行结果:
为什么实现了Runnable接口还需要调用Thread类中的start()方法才能启动多线程呢?这是因为Runnable接口中只有一个run()方法。也就是说,Runnable接口中并没有start()方法,所以一个类实现了RunnableJ接口也必须用Thread类中的start()方法启动多线程。
3 两种多线程实现机制的比较
从上面的售票实例可以看出,通过继承Thread类实现多线程时,要创建4个线程对象:
NanJingStation s1=new NanJingStation();
NanJingStation s2=new NanJingStation();
NanJingStation s3=new NanJingStation();
NanJingStation s4=new NanJingStation();
s1.start();
s2.start();
s3.start();
s4.start();
实际上是创建了4个线程对象——“南京站”,每个“南京站”有100张票。这4个线程对象各自占用各自的资源,无法达到资源共享的目的。再看通过实现Runnable接口实现多线程的方式,尽管也创建了4个线程对象(窗口),但是这里只建立了一个“南京站”,在这个“南京站”里开启了4个线程对象——窗口。它们都是操作同一个资源(“南京站”中的100张票),从而实现了资源共享的目的。
NanJingStation s=new NanJingStation();
Thread t1=new Thread(s,"窗口1");
Thread t2=new Thread(s,"窗口2");
Thread t3=new Thread(s,"窗口3");
Thread t4=new Thread(s,"窗口4");
//第五步:开启线程
t1.start();
t2.start();
t3.start();
t4.start();