两种线程实现方法的比较

1.两种线程实现方法的比较

(1)实现线程的方法一

线程类继承Thread类,重写run()方法,见代码12.6的第1行到第6行。在main线程中,先创建线程类对象,然后调用start()方法启动线程,见代码12.6的第9行、第10行。

【代码12.6】

(2)实现线程的方法二

任务类实现Runnable接口和run()方法,见代码12.7的第1行到第6行。在main线程中,先创建任务类对象,再以任务类对象为传入参数创建Thread线程对象,然后对线程对象调用start()方法启动线程,见代码12.7的第9行到第11行。

【代码12.7】

两种实现线程的方法在多道线程各自独立没有关联的时候,运行效果是相同的,采用哪种方法都可以。但是,因为Java是单继承(一个类最多只能继承一个父类),所以当需要继承其他父类时,就只能采用方法二。

当多道线程出现数据共享时,两种方法的运行情况会不同,请看如下案例:车站当前共有车票数5张,分别有3个窗口同时并行卖票,用多线程模拟卖票过程。

【代码12.8】采用线程实现方法一

代码12.8的运行结果:

第一窗口卖掉了第5张票

第三窗口卖掉了第5张票

第二窗口卖掉了第5张票

第三窗口卖掉了第4张票

第一窗口卖掉了第4张票

第三窗口卖掉了第3张票

第二窗口卖掉了第4张票

第三窗口卖掉了第2张票

第一窗口卖掉了第3张票

第三窗口卖掉了第1张票

第二窗口卖掉了第3张票

第一窗口卖掉了第2张票

第一窗口卖掉了第1张票

第二窗口卖掉了第2张票

第二窗口卖掉了第1张票

我们可以发现运行结果是不正确的,在这个运行结果中,不是3个窗口一起卖5张票,而是每个窗口各自卖5张票。那么怎样让3个窗口共享5张票呢?请看第二种实现方法。

【代码12.9】采用线程实现方法二

代码12.9的运行结果:

第一窗口卖掉第4张票

第三窗口卖掉第3张票

第二窗口卖掉第5张票

第三窗口卖掉第1张票

第一窗口卖掉第2张票

方法二不会像方法一那样,每个窗口各卖5张票,方法二是三个窗口一起卖5张票。在方法一中,每个线程各自有一个tickets成员,所以3道线程没有共享tickets。在方法二中,只创建了一个任务类对象,3道线程共享同一个任务类对象,这样也就共享了同一个任务中的tickets。因此,在多线程之间有数据共享的时候,要采用方法二。但是,方法二还有可能会出现问题,其运行结果可能会出现一张票被重复卖多次的不合理情况,这涉及后面要讲的线程不安全。