因为售票的例子博友们已经列举太多了,主要取决于是否采用new Thread(Runnable target, String name)构造创建实例,如果不想看例子的,直接看结果分析吧
1.Thread和Runuable的关系
public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {
…
}
从这两段代码我们可以知道Runnable是一个接口,而Thread实现了这个接口,即Thread是Runnable的实现类。
2.Thread和Runnable的比较
当要联网或处理耗时逻辑的时候,我们要开启子线程,那选用那种方式好呢?Runnable具有以下优势:
(1)java不支持多继承,实现Runnable还可以继承其它类,调用其功能,更具灵活性
(2)增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
(3)适合多个相同的程序代码的线程去处理同一个资源(有很多博友有这样的说法,个人存疑,因为Thread方式也可以达到同样目的)
还是用售票的例子吧,话不多说,上代码:
/**
* Time: xxx
* Author: xxx
* Description: 继承Thread方式实现多线程并发售票
*/
public class TicketThread extends Thread {
//4张票
private int ticket = 4;
@Override
public void run() {
for (int i = 0; i < 4; i++) {
synchronized (this) {
if (this.ticket>0) {
try{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"卖票--->"+(ticket--));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
TicketThread ticketThread = new TicketThread();
new Thread(ticketThread, "线程1").start();
new Thread(ticketThread, "线程2").start();
new Thread(ticketThread, "线程3").start();
}
}
结果如下:
线程2卖票—>3
线程3卖票—>1
线程1卖票—>2
/**
* Time: xxx
* Author: xxx
* Description: 实现Runnable的方式实现多线程卖票
*/
public class TicketRunnable implements Runnable {
private int ticket = 3;
@Override
public void run() {
for (int i = 0; i < 3; i++) {
synchronized (this) {
if (this.ticket>0) {
try{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"卖票--->"+(ticket--));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
TicketRunnable ticketRunnable = new TicketRunnable();
new Thread(ticketRunnable, "线程1").start();
new Thread(ticketRunnable, "线程2").start();
new Thread(ticketRunnable, "线程3").start();
}
}
线程2卖票—>3
线程3卖票—>1
线程1卖票—>2
有个小细节要注意一下,一定要加上synchronized同步锁,否则可能会出现其中一个线程出现卖票—>0的情况,这很好理解,如果没加synchronized,也就是线程不安全,当其中一个线程卖最后一张票ticket=1执行ticket–的时候此时ticket已经为0了,但是另一个线程已经执行完了if(this.ticket>0),再执行ticket–的时候就会出现卖票—>0的情况的情况。这里就不演示了,有兴趣的博友可以自己试一下,如果一次没出现,就运行几次,总会中奖的。
至于博友们采用以下的写法:
public class TicketThread extends Thread {
//3张票
private int ticket = 3;
@Override
public void run() {
for (int i = 0; i < 3; i++) {
if (this.ticket>0) {
try{
Thread.sleep(100);
System.out.println("ticket= "+ticket--);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
TicketThread ticketThread = new TicketThread();
new TicketThread().start();
new TicketThread().start();
new TicketThread().start();
}
}
结果:
ticket= 3
ticket= 3
ticket= 3
ticket= 2
ticket= 2
ticket= 2
ticket= 1
ticket= 1
ticket= 1
3. 结果分析
并不是说Runnable支持资源的共享,而Thread不支持。
本质是:构造方法传参的问题,是要传参,持有Runnable的引用;还是不传参,new新的对象
以这种方式使用Thread(不传参)时,每一个New都会产生一个拥有自己的ticket的对象,即每个对象都有各自的ticket,各操作各的,三个ticket数值变化互不影响。就相当于new了三个对象,把方法调用了三遍。如果传参,同样可以达到“资源共享”的效果。在使用Runnable的整个过程中(传参了),只产生一个能操作ticket的对象,实现多个线程共同处理同一资源(ticket),即实现资源的共享;