1、利用子类继承Thread类创建线程,模拟三个线程购票:
class Windows extends Thread{
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket > 0){
System.out.println("您购买的票号为:" +ticket);
ticket--;
}else
break;
}
}
}
public class ThreadTest {
public static void main(String[] args) {
Windows my1 = new Windows();
Windows my2 = new Windows();
Windows my3 = new Windows();
my1.start();
my2.start();
my3.start();
}
}
出现问题:
在这三个线程中出现相同票号,且总票数为300,原因为总票数100在每创建一个线程都通过start()方法默认使用了这100张票,此时尝试在100前添加static修饰符,但还是会有重票现象。线程安全问题待解决。
2、通过实现Runnable接口创建线程:
class Windows implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if (ticket > 0){
System.out.println("您购买的票号为:" +ticket);
ticket--;
}else
break;
}
}
}
public class RunnableTest {
public static void main(String[] args) {
Windows windows = new Windows();
Thread t1 = new Thread(windows);
Thread t2 = new Thread(windows);
Thread t3 = new Thread(windows);
t1.start();
t2.start();
t3.start();
}
}
首先通过代码可以发现与方式一创建线程不同的是,ticket没有用static修饰,但是也没有出现和方式一样产生了300总票,原因是只new了一个Windows对象,线程的创建是通过new Thread方法实现的。但是通过Thread如何能调用到Windows中重写的run()呢?通过查看源码可以发现在Thread类中构造器传入了一个target参数,而target参数为runnable引用类型,若target参数不为空,则调用target.run()。但是此方法创建线程依旧存在线程安全问题待解决。
3、两种创建线程方式的差异:
在开发中优先选择实现Runnable接口的方式,原因有:① 在开发中,Windows类往往会需要继承自己的父类,但是受限于Java的单继承规则,这就对通过继承Thread类来创建线程有了一定的局限性。②通过实现接口方式创建线程更加适合处理多个线程之间有共享数据的情况。这两种创建线程的方式有所联系,因为通过源码可知,第一种线程创建方法,Thread类其实也是实现了Runnable接口并重写了Runnable接口内的run()方法,只不过通过子类的继承第二次重写了run()方法,第二种相对于第一种线程创建,相当于没有中间商。