在之前剖析中,我们知道线程从NEW状态进入到RUNNABLE装须要调用start()方法,但是当线程调用完start()方法之后到底进行了那些操作呢?在之前的例子中,我们将业务逻辑代码写到了run()方法中,但是实际上实行线程调用的时候却是实行的是线程的start()方法,两者之间又有什么联系呢?
下面我们就来剖析一下。
Thread 类的start方法源码如下所示。

public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } }
通过源码可以看出来,源码逻辑相比拟较大略,个中最紧张的一个代码便是start0()方法。在源码中可以看到start0(),代码如下所示。
private native void start0();
也便是说,在我们调用了start()方法之后,会调用start0()方法,还是没有创造run()方法是什么时候被实行的,但是查看JDK的文档之后,我们会知道,实在这里的start0()方法实在是通过JNI技能进行调用的,而run()方法也是被JNI的start0()方法。阅读源码之后会有如下的一些问题。
当我们new一个线程之后,threadStatus 属性的值该当是默认为0的,当不即是0的时候就会抛出非常。这个在源码中说得比较清楚。threadStatus为零表示这个当前状态是NEW根据threadStatus的状态可以知道,不能进行两次启动线程操作,也便是说同一个线程工具,不能两次重复调用start()方法,否则就会抛出非常。线程启动之后就会被加入到一个线程组中,后续会说到线程组干系的观点。当一个线程结束,进入到TERMINATED状态之后,是没有办法通过调用start()方法再次进入到RUNNABLE状态或者是RUNNING状态的。模板设计模式在Thread中的利用通过上面的剖析,可以知道,一个线程真正的逻辑实行代码是被放到run()方法中,常日情形下run()方法便是我们编写线程实行逻辑的地方,这也便是为什么我们要重写run()方法,用start()启动的线程。Thread类中的run方法如下。
@Override public void run() { if (target != null) { target.run(); } }
默认情形下,这个方法可以看做是一个空方法。方法体中没有任何代码。
不难创造在Thread中的run()方法start()方法便是一个非常范例的模板设计模式,父类编写算法构造代码,子类卖力按照对应的模板进行实现。下面来看一个小例子
模板设计模式小例子首先创建一个输出的接口类,这个类便是制订输出的规则,代码如下
public interface PrintMessage { abstract void print();}
接下来便是其子类要实现这个输出了。代码如下
public class Repoter implements PrintMessage { public void printNews() { print(); } @Override public void print() { System.out.println("show news"); }}
编写测试类main方法。
public class TemplateMain { public static void main(String[] args) { Repoter repoter = new Repoter(){ @Override public void print() { System.out.println("这个是一个设计模板方法"); } }; repoter.printNews(); }}
这里会看到全体的模式都是按照Thread的模式来进行。个中的print()方法和printNews()方法就类似于Thread类的run()方法和start()方法。这样一来,父类只须要见告子类须要做的事情是什么,而不须要知道至于子类如何实现,终极便是子类的事情了。
大略的仿照一个叫号系统很多人都有过存款或者买票的体验,他们有一个共同的特点便是须要排队叫号。实在这种机制办理了很多的问题。例如可以做到限流,可以合理的分配处理职员的位置,可以很好的办理了由于排队引起的社会抵牾。同时也减轻了业务员、管理职员的压力
我们这里大略的仿照有四个窗口的叫号系统。四个窗口就相称于有四个线程。通过这四个窗口来仿照50人排队的场景。
public class TicketWindow extends Thread { private final String name; private static final int MAX = 50; private int index = 1; public TicketWindow(String name) { this.name = name; } @Override public void run() { while (index<MAX){ System.out.println(name+" 号柜台,出号 "+index++); } } public static void main(String[] args){ TicketWindow ticketWindow1 = new TicketWindow("一"); ticketWindow1.start(); TicketWindow ticketWindow2 = new TicketWindow("二"); ticketWindow2.start(); TicketWindow ticketWindow3 = new TicketWindow("三"); ticketWindow3.start(); TicketWindow ticketWindow4 = new TicketWindow("四"); ticketWindow4.start(); }}
运行上面代码之后会看到如下的结果,大略地剖析之后,会创造实在这是有问题的。1号票被多个柜台出了。
为什么会涌现这样的问题,这便是每个线程实行的逻辑都不一样,新建了四个线程,每个线程的票号都是从0~50。四个线程并没有操作同一个票号排序。以是就会涌现上面这个问题。那么我们如何去办理这个问题呢?我们想到了利用static 去润色 index变量的办法。代码如下。
public class TicketWindow extends Thread { private final String name; private static final int MAX = 50; private static int index = 1; public TicketWindow(String name) { this.name = name; } @Override public void run() { while (index<MAX){ System.out.println(name+" 号柜台,出号 "+index++); } } public static void main(String[] args){ TicketWindow ticketWindow1 = new TicketWindow("一"); ticketWindow1.start(); TicketWindow ticketWindow2 = new TicketWindow("二"); ticketWindow2.start(); TicketWindow ticketWindow3 = new TicketWindow("三"); ticketWindow3.start(); TicketWindow ticketWindow4 = new TicketWindow("四"); ticketWindow4.start(); }}
经由修正之后,运行结果如下,这个时候看上去统统都就正常了。
通过对index进行static的润色,做到了多线程共享索引的操作。看上去是知足了需求,但是实际上还是有很多的问题,如果共享索引的数据太大的时候,也会涌现线程安全的问题。这个问题在后续的分享中会提到。以是说static只能办理小问题,须要办理大问题的话还须要大手笔的办理方案。在后续的分享中也会提到。这里就先到这里,大家可以先消化一下Thread 的模板设计模式事理。