线程的状态
准确来说,应该有五个状态。
被创建后通过start()进入到运行状态。执行完run()方法或者调用stop()方法,会从运行状态进入到消亡状态。而运行状态,又会随时因为CPU的切换,进入到临时阻塞状态,或者叫“挂起”。如果在运行时期,调用sleep()或者wait()方法,会使线程进入冻结状态。当休眠结束或者调用notify()方法,会让线程回到运行状态,或者是临时阻塞状态。
多线程的创建
有两种方式,第一种是继承Thread类,第二种是实现Runnable接口。
继承Thread类
第一种方式继承Thread类,将需要多线程处理的业务放到Thread类的run方法中。直接创建继承后的类对象,使用start()方法,即可开启多线程。
class MultiThreadDemo extends Thread{
private String name;
MultiThreadDemo(String name){
this.name=name;
}
@Override
public void run() {
for(int x=0;x<100;x++){
System.out.println("任务:"+name+"...执行第"+x+"次...Thread-name:"+Thread.currentThread().getName());
}
}
}
public class MulitThreadTest {
public static void main(String[] args) {
MultiThreadDemo mtd1=new MultiThreadDemo("gg");
MultiThreadDemo mtd2=new MultiThreadDemo("哈哈");
mtd1.start();
mtd2.start();
}
}
这种方式的弊端在于:①获得了Thread中不需要的方法。②仅仅因为需要开启多线程,就继承了Thread了,成为了Thread类体系,而这个类本来跟Thread类毫不相干。③一旦继承了Thread类,就不能继承其它类了。
实现Runnable接口
第二种方式实现Runnable接口,覆写run方法。在调用的时候,把实现了该接口的类作为参数,传到Thread类的构造函数中,最后调用Thread对象的start()方法即可。
class MultiThreadDemo implements Runnable{
private String name;
MultiThreadDemo(String name){
this.name=name;
}
@Override
public void run() {
for(int x=0;x<100;x++){
System.out.println("任务:"+name+"...执行第"+x+"次...Thread-name:"+Thread.currentThread().getName());
}
}
}
public class MulitThreadTest {
public static void main(String[] args) {
MultiThreadDemo mtd1=new MultiThreadDemo("哈哈");
MultiThreadDemo mtd2=new MultiThreadDemo("gg");
Thread t1=new Thread(mtd1);
Thread t2=new Thread(mtd2);
t1.start();
t2.start();
}
}
这种方式的优点在于:①避免了单继承的局限。②直接将具体任务封装成了具体的线程对象。
卖票示例
四个机器(线程)同时卖100张票。下面这种使用继承Thread的方式,创建四个Ticket对象,肯定是不行的,各卖各的。
Ticket ticket1=new Ticket();
Ticket ticket2=new Ticket();
Ticket ticket3=new Ticket();
Ticket ticket4=new Ticket();
ticket1.start();
ticket2.start();
ticket3.start();
ticket4.start();
票是公有的,要被所有线程共享,一种方式是将票数变成静态的。另外一种是实现Runnable接口,四个线程用同一个对象。
class Ticket implements Runnable{
private int num=100;
@Override
public void run() {
while(true){
if(num>0){
System.out.println(Thread.currentThread().getName()+"....sale..."+num--);
}
}
}
}
public class TicektSell {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread t1=new Thread(ticket);
Thread t2=new Thread(ticket);
Thread t3=new Thread(ticket);
Thread t4=new Thread(ticket);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
这样,似乎就实现了四个机器同时卖100张票。
线程安全问题
上面的代码,如果在if判断里面加上一条线程睡眠,就会出现“线程安全”问题。
if(num>0){
try {
Thread.sleep(100);
}catch (InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"....sale..."+num--);
}
票卖到了0,还在继续卖,甚至出现了负数!
原因在于:假设此时票数=1,线程0进行if判断,为true,进入到if代码块。这个时候,突然CPU切换线程,线程0被挂起。此时轮到线程1进行if判断,票数还是1,依然可以进入到if代码块。这个时候,CPU再次切换,线程1也被挂起,轮到线程2进行if判断,还是可以进去。
接下来,CPU切回线程0,线程0将票数-1,此时票数为0。再切回线程1,线程1继续-1,票数变成了-1。最后切回线程2,再-1,变成了-2。这样就出现了线程安全问题。
对于线程安全问题,做法就是加锁,我这里没处理完,你就不要进来。