通过一个案例来实现多线程,例如一个卖票系统,我们假设有四个窗口同时卖票,因此可以构造四个线程来实现:
class Ticket extends Thread{
private int num=100; //共有100张票
public void run()
{
while(true)
{
if(num>0){
//Thread.currentThread().getName()获取当前正在运行的线程的名字
System.out.println(Thread.currentThread().getName()+"卖"+num--+"号票");
}
}
}
}
public class Demo {
public static void main(String[] args) {
//创建四个线程
Ticket t1=new Ticket();
Ticket t2=new Ticket();
Ticket t3=new Ticket();
Ticket t4=new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果(部分)如下:
我们可以看到四个线程卖完了100张票,但是发现每个线程都卖了1-100号共100张票,而现实情况下应该是四个线程一共卖1-100号共100张票,但我们的运行结果却一共卖了400张票,这是因为每个线程的run()方法中都有num=100,每个线程都要执行num--的循环,即每个线程的run()方法都要执行从100至1的循环。所以四个线程共执行400个循环。
为了解决这一问题,即四个线程共享同一数据,我们可将变量num设置为静态的:
private static int num=100;
运行结果(部分):
但是在现实中,num等这类数据不一定要实现共享(即不再静态化),所以我们要寻求另外的解决办法:
不一定要实现共享:指在一个程序中,可能有两个线程共享一个数据,另外两个线程共享另一个数据,这种情况下不能使用静态。
这时我们不再使用继承,通过实现Runnable接口(只封装线程任务)来解决这一问题:
class Ticket implements Runnable{
private int num=100; //共有100张票
public void run()
{
while(true)
{
if(num>0){
//Thread.currentThread().getName()获取当前正在运行的线程的名字
System.out.println(Thread.currentThread().getName()+"卖"+num--+"号票");
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket t=new Ticket(); //创建一个线程任务对象
//创建线程
Thread t1=new Thread(t); //任务对线程进行初始化
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果(部分):
这时在main函数中只创建了一个任务对象,所以只有一个数据成员num=100, 每个线程在运行run()方法时都使用的是这一个对象的num,即四个线程共享这一个数据。
引申:
在前面我们提到,数据不能实现共享,例在一个程序中,有几个数据要分别被几个线程共享,也可以通过实现Runnable接口来解决这个问题:
假设,有100张火车票被两个窗口(线程)共享,另外有100张动车票被三个窗口(线程)共享,在main中这样定义:
public class Demo {
public static void main(String[] args) {
//创建两个线程任务对象(即两种票)
Ticket t=new Ticket();
Ticket tt=new Ticket();
//创建线程
Thread t1=new Thread(t); //t1、t2共享对象t的num
Thread t2=new Thread(t);
Thread t3=new Thread(tt); //t3、t4、t5共享对象tt的num
Thread t4=new Thread(tt);
Thread t5=new Thread(tt);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}