两种实现多线程方式的对比分析

既然直接继承 Thread类和实现Runnable接口都能实现多线程,那么这两种实现多线程的方式在实际应用中又有什么区别呢?接下来通过一种应用场景来分析

假设售票厅有四个窗口可发售某日某次列车的100张车票,这时,100张车票可以看作共享资源,四个售票窗口需要创建四个线程。为了更直观显示窗口的售票情况,可以通过 Thread的currentThread()方法得到当前的线程的实例对象,然后调用getName()可以获取到线程的名称。接下来,首先通过继承Thread类的方式来实现多线程的创建。

public class Example1 {
    
    
	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--+" 张票 ");
			}
		}
	}
}

运行结果:
在这里插入图片描述
从上面的运行结果可以看出,每张票都被打印了四次。出现这种现象的原因是四个线程没有共享100张票,而是各自出售了100张票。在程序中创建了四个Ticket Window对象,就等于创建了四个售票程序,每个程序中都有100张票,每个线程在独立地处理各自的资源。需要注意的是,例5-4中每个线程都有自己的名字,主线程默认的名字是“main”,用户创建的第一个线程默认的名字为“Thread-0”,第二个线程的默认名字为“Thread-1”,依此类推。如果希望指定线程的名称,则可以通过调用setName(String name)为线程设置名称。

由于现实中铁路系统中的票资源是共享的,因此上面的运行结果显然不合理。为了保证资源共享,在程序中只能创建一个售票对象,然后开启多个线程去运行同一个售票对象的售票方法,简单来说就是四个线程运行同一个售票程序,这时就需要用到多线程的第二种实现方式。将 上例进行修改,并使用构造方法 Thread (Runnable target,String name)在创建线程对象的同时指定线程的名称。

public class Example05 {
    
    
	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-- + " 张票 ");
			}
		}
	}
}

运行结果:
在这里插入图片描述
例2中只创建了一个 TicketWindow对象,然后创建了四个线程,在每个线程上都去调用这个TicketWindow对象中的run()方法,这样就可以确保四个线程访问的是同一个tickets变量,共享100张车票。
通过上面的两个例程可以看出,实现 Runnable接口相对于继承 Thread类来说,有如下显著的好处:

  1. 适合多个相同程序代码的线程去处理同一个资源的情况,把线程同程序代码、数据有效的分离,很好地体现了面向对象的设计思想。
  2. 可以避免由于Java的单继承带来的局限性。在开发中经常碰到这样一种情况,就是使用一个已经继承了某一个类的子类创建线程,由于一个类不能同时有两个父类,所以不能用继承 Thread类的方式,那么就只能采用实现Runnable接口的方式。

事实上,大部分的应用程序都会采用第二种方式来创建多线程,即实现Runnable接口。

猜你喜欢

转载自blog.csdn.net/xun08042/article/details/113513714