线程的创建(Runnable,Thread)

一种时继承java.lang包下的Thread类,另一种是实现java.lang.Runnable接口。


一、继承Thread类创建多线程

①先看一个单线程程序,单线程程序的线程是进程默认创建,在代码中看不到。

当一个Java程序启动时,就会产生一个进程,该进程会默认创建一个线程,在这个线程上会运行main()方法中的代码

public class Example01 {
	public static void main(String[] args) {
		Lei p=new Lei(); // 创建Lei实例对象,这不是线程对象,只是一个普通的类的对象
		p.run();  // 调用Lei类的run()方法
		while (true) {  // 该循环是一个死循环,打印输出语句
			System.out.println("Main方法在运行");
		}
	}
}
class Lei {
	public void run() {
		while (true) { // 该循环是一个死循环,打印输出语句
			System.out.println("Lei类的run()方法在运行");
		}
	}
}

结果:一直输出:“Lei类的run()方法在运行”,“Main方法在运行”不会输出,因为前面语句一直霸占这cpu.

②通过 继承 Thread 类实现多线程

public class Example {
	public static void main(String[] args) {
		NewThread newThread = new NewThread(); // 创建线程MyThread的线程对象
		newThread.start();                      // 开启线程
		while (true) {                          // 通过死循环语句打印输出
			System.out.println("main()方法在运行");
		}
	}
}
class NewThread extends Thread {
	public void run() {
		while (true) {    // 通过死循环语句打印输出
			System.out.println("MyThread类的run()方法在运行");
		}
	}
}
结果:两条语句交叉输出

二、实现Runnable接口创建多线程

①因为Java只支持单继承,一个类一旦继承了某一个父类就无法再继承Thread类若Student类继承了Person类之后就不能再继承Thread类了,所以继承Thread类创建多线程具有一定的局限性

②为了克服这个问题,Thread类提供了另外一个构造方法Thread(Runnable target), Runnable是一个接口,它只有一个run()方法。

当使用这个构造方法时,需要为这个方法传递一个实现了Runnable接口的实例对象,然后创建的线程将调用实现了Runnable接口中的run()方法作为运行代码,而不需要调用Tread类中的run()方法。

如下面代码

public class Example {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();  // 创建MyThread的实例对象    
		Thread thread=new Thread(myThread);  // 创建线程对象
		thread.start();                          // 开启线程,执行线程中的run()方法
		while (true) {
			System.out.println("main()方法在运行");
		 }
	   }
}
class MyThread implements Runnable {
	public void run() {          // 线程的代码段,当调用start()方法时,线程从此处开始执行
		while (true) {
			System.out.println("MyThread类的run()方法在运行");
		}
	}
}

三、两种方式的对比

假设有200张飞机票,这时,200张飞机票可以看作共享资源,4个售票窗口需要创建4个线程。通过这个案例比较上述两种方式创建多线程的不同

其中为了更直观的显示窗口的售票情况,通过Tread的currentThread()方法得到当前的线程的实例对象,然后调用getName()可以获取到线程的名称。

1、通过继承Thread类实现多线程

public class Example {
	public static void main(String[] args) {
		new TicketWindow().start();   // 创建一个线程对象TicketWindow并开启
		new TicketWindow().start();   // 创建一个线程对象TicketWindow并开启
		new TicketWindow().start();   // 创建一个线程对象TicketWindow并开启
		new TicketWindow().start();   // 创建一个线程对象TicketWindow并开启
	}
}
class TicketWindow extends Thread {
	private int tickets = 100;
	public void run() {
		while (true) {    // 通过死循环语句打印语句
			if (tickets > 0) {
				Thread th = Thread.currentThread(); // 获取当前线程
				String th_name = th.getName();       // 获取当前线程的名字
				System.out.println(th_name + " 正在发售第 " + tickets--+" 张票 ");
			}
		}
	}
}
结果:

线程的创建(Runnable,Thread)_第1张图片
由结果可以看到票会重复出现,都被打印了四次,出现这种现象的原因时四个线程没有共享100张票,而是各自出售了100张票,在程序中创建了四个TicketWindow对象,就等于创建了四个售票程序,每个程序都有100张票,每个线程都在独立的处理各自的资源。

每个线程我们都可以自定义名字,这里用了默认的名字,主线程默认的名字是main,用户创建的第一个线程默认的名字为Thread-0,第二个是Thread-1.以此类推。


2、实现Runnable接口创建多线程

public class Example {
	public static void main(String[] args) {
		TicketWindow tw = new TicketWindow(); // 创建TicketWindow实例对象tw
		new Thread(tw, "窗口1").start();      // 创建线程对象并命名为窗口1,开启线程
		new Thread(tw, "窗口2").start();      // 创建线程对象并命名为窗口2,开启线程
		new Thread(tw, "窗口3").start();      // 创建线程对象并命名为窗口3,开启线程
		new Thread(tw, "窗口4").start();      // 创建线程对象并命名为窗口4,开启线程
	}
}
class TicketWindow implements Runnable {
	private int tickets = 100;
	public void run() {
		while (true) {
			if (tickets > 0) {
				Thread th = Thread.currentThread(); // 获取当前线程
				String th_name = th.getName(); // 获取当前线程的名字
				System.out.println(th_name + " 正在发售第 " + tickets-- + " 张票 ");
			}
		}
	}
}
结果:

线程的创建(Runnable,Thread)_第2张图片

这个结果与现实相一致,实现Runnable接口创建多线程中,只创建了一个对象,然后在一个对象里创建了四个线程,在每个线程上都去调用这个Ticket变量,共享这100张车票。而通过继承Thread类实现的多线程是创建多个对象,一个对象一个线程。大多数情况会用第二种情况。

3、实现Runnable接口创建多线程优点:

①避免Java单继承带来的局限性(一个类一旦继承了某一个父类就无法再继承Thread类

②适合多个相同程序代码的线程去处理同一个资源的情况。


四、前台线程与后台线程

1、前台线程

①当一个Java程序启动时,就会产生一个进程,该进程会默认创建一个线程,在这个线程上会运行main()方法中的代码,这个线程为前台线程。

②新创建的线程都默认为前台线程。

2、后台线程

当某个线程在启动之前调用了setDeamon(true)语句,这个线程就变成了一个后台线程。下面为例子

class DamonThread implements Runnable { // 创建DamonThread类,实现Runnable接口
	public void run() { // 实现接口中的run()方法
		while (true) {
			System.out.println(Thread.currentThread().getName()
					+ "---is running.");
		}
	}
}
public class Example2 {
	public static void main(String[] args) {
         System.out.println("main线程是后台线程吗?"+ Thread.currentThread().isDaemon());
		DamonThread dt = new DamonThread();      // 创建一个DamonThread对象dt
		Thread t = new Thread(dt,"后台线程");    // 创建线程t共享dt资源
         System.out.println("t线程默认是后台线程吗? "+t.isDaemon()); // 判断是否为后台线程
		t.setDaemon(true);           // 将线程t设置为后台线程
		t.start();                    // 调用start()方法开启线程t
		for(int i=0;i<10;i++){ 
			System.out.println(i);
		}
		System.out.println("前台进程结束.end------");
	}
}
结果: 线程的创建(Runnable,Thread)_第3张图片
此例中,将线程t设置为后台线程

若t为前台进程,当开启线程t后,会执行死循环中的打印语句,但将线程t设置为后台线程后,当前台线程死亡后,JVM会通知后台进程,停止执行,由于从接受指令到做出相应,需要一定的时间,因此,会打印几次“后台进程---is running.”,结束程序。


你可能感兴趣的:(线程的创建(Runnable,Thread))