创建线程的第一种方式:
继承Thread类。
步骤:
1,定义一个Thread类的子类。
2,复写Thread类中的run方法。将需要线程运行的代码存储到该run方法中。
run方法的唯一功能是:存储线程要运行的代码。
3,创建Thread类的子类对象。创建线程。
4,调用Thread类中的start方法开启线程并运行子类的run方法。
*/
class Demo extends Thread
{
public void run()
{
for(int x=0;x<100; x++)
{
System.out.println("zzzzzzz----"+x);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//Thread t = new Thread();//创建了一个线程对象。
/*
注意:线程的创建和运行是两个。运行是通过start方法完成的。
*/
//t.start();//开启线程执行。
/*
发现start方法有两个动作。
1,开启线程,让线程执行。开启线程的目的其实就是为了让这条执行路径去执行指定代码。
2,告诉jvm调用这个线程的run方法。 猜想,run方法,是不是就意味着内部封装了线程要执行的代码呢?
发现run方法没有执行任何动作就返回了。
注意:我们开启线程,是为了让线程去执行我们指定的代码。
所以Thread类就给我们提供了一个线程要运行的代码存放的位置。
但是要运行什么代码Thread并不知道。
所以我们需要将要运行的代码存储到run方法里面。
那不是就沿袭父类中的功能,定义子类特有的内容。
就是覆盖调用父类的方法即可。
//创建线程。
Demo d = new Demo();//创建Thread类的子类对象,也是创建线程。
d.start();
// d.run();//和start有区别吗?调用run就只有一个线程main在运行,而且run中的代码都由main线程运行的。
//创建的新线程根本就没有运行,所以只有主线程在运行。
//start是先开启线程,然后让新的线程去运行自己的run方法。
for(int x=0;x<100; x++)
{
System.out.println("aaaaa.."+x);
}
}
}
线程创建的第二种方式:
实现Runnable接口。
步骤:
1,定义一个Runnable接口的子类。
2,覆盖Runnable接口的run方法。
3,通过Thread类创建线程对象。
4,将实现Runnable接口的子类对象作为参数传递给Thread类的构造函数。
为什么要这么做呢?
因为要告诉线程对象要运行哪个run方法。
现在线程要执行的代码都存储到了Runnable接口的子类对象中。
那么线程要运行这个run方法。必须要明确这个run方法所属的对象。
所以将该对象传递给Thread构造函数。这样线程对象就可以去执行指定的run方法。
5,通过线程对象的start开启线程并执行。
这种方式的好处:避免了单继承的局限性。
所以通常情况下,创建线程,都使用实现Runnable接口的方式。
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<100; x++)
{
System.out.println(Thread.currentThread().getName()+"....qqqqqq....."+x);
}
}
}
class ThreadDemo3
{
public static void main(String[] args)
{
Demo d = new Demo();
Thread t1 = new Thread(d);//创建线程对象。
Thread t2 = new Thread(d);
t1.start();//开启两个线程。
t2.start();
/*
创建了两个线程对象,都用start开启了,但是没有结果。
那是因为,start方法开启线程,并执行了线程的run方法。
*/
}
} |