初见Java多线程(二、线程的创建与启动)

来源:互联网 发布:openwrt防网络尖兵 编辑:程序博客网 时间:2024/04/23 15:32

创建线程的第一种方式:

继承Thread ,由子类复写run方法。

步骤:

1、定义类继承Thread类

2、目的是复写run方法,将要让线程运行的代码都存储到run方法中 

3、通过创建Thread类的子类对象,创建线程对象

4、调用线程的start方法,开启线程,并执行run方法。


实现代码如下:

public class ExtendsThread {public static void main(String[] args) {// 实例化线程对象MyThread mt = new MyThread();// 为新建mt线程设置名称mt.setName("MyThread");// 启动线程mt.start();for(int i=0; i<10; i++){System.out.println(Thread.currentThread().getName() + "---->" + i);}}}class MyThread extends Thread {public void run() {for (int i = 0; i < 10; i++) {// Thread.currentThread().getName() 获取当前线程的名称System.out.println(Thread.currentThread().getName() + "---->" + i);}}}

创建线程的第二种方式:

实现一个接口Runnable。

步骤: 

  1、定义类实现Runnable接口。

  2、覆盖接口中的run方法(用于封装线程要运行的代码)。

  3、通过Thread类创建线程对象;

  4、将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。

 5、调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。


实现代码如下:

public class RunnableThread {public static void main(String[] args) {// 直接创建MyThread对象,并不是创建线程对象。MyThread mt = new MyThread();// 因为创建对象只能通过new Thread类,或者new Thread类的子类才可以。 所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。Thread thread = new Thread(mt);// 为新建thread线程设置名称thread.setName("MyThread");// 启动线程thread.start();for(int i=0; i<10; i++){System.out.println(Thread.currentThread().getName() + "---->" + i);}}}class MyThread implements Runnable{public void run() {for(int i=0; i<10; i++){// Thread.currentThread().getName() 获取当前线程的名称System.out.println(Thread.currentThread().getName() + "---->" + i);}}}


启动线程:

在线程的Thread对象上调用start()方法,而不是run()或者别的方法。
在调用start()方法之前:线程处于新状态中,新状态指有一个Thread对象,但还没有一个真正的线程。
 
在调用start()方法之后:发生了一系列复杂的事情
1、启动新的执行线程(具有新的调用栈);
2、该线程从新状态转移到可运行状态;
3、当该线程获得机会执行时,其目标run()方法将运行。
 
注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。

一些常见问题:

1、线程的名字,一个运行中的线程总是有名字的,名字有两个来源,一个是虚拟机自己给的名字,一个是你自己的定的名字。在没有指定线程名字的情况下,虚拟机总会为线程指定名字,并且主线程的名字总是mian,非主线程的名字不确定。

2、线程都可以设置名字,也可以获取线程的名字,连主线程也不例外。

3、获取当前线程的对象的方法是:Thread.currentThread();

4、在上面的代码中,只能保证:每个线程都将启动,每个线程都将运行直到完成。一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说,调度程序不能保证其执行次序,持续时间也无法保证。

5、当线程目标run()方法结束时该线程完成。

6、一旦线程启动,它就永远不能再重新启动。只有一个新的线程可以被启动,并且只能一次。一个可运行的线程或死线程可以被重新启动。

7、线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。
众多可运行线程中的某一个会被选中做为当前线程。可运行线程被选择运行的顺序是没有保障的。

8、尽管通常采用队列形式,但这是没有保障的。队列形式是指当一个线程完成“一轮”时,它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止,它才能被再次选中。事实上,我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不都是以某种有保障的顺序排列唱呢个一个队列的事实。

9、尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。



为什么要有Runnable接口的出现?

一、  因为实现Runnable接口可以避免单继承的局限性。

二、

其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提

所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。

而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。

所以Runnable接口将线程要执行的任务封装成了对象。   

  

原创粉丝点击