第一个多线程程序
创建线程
假设银行大厅共有四台出号机,即有四个线程在工作。用程序模拟叫号的过程,约定当天最多受理8笔业务。
继承Thread
/**
* 柜台叫号:继承Thread、重写run方法
*/
public class TicketWindow extends Thread { // 1.继承Thread类
// 柜台名称
private final String name;
// 最多受理50笔业务
private final int MAX = 8;
private int index = 1;
public TicketWindow(String name) {
this.name = name;
}
// 2.重写run方法
@Override
public void run() {
while (index <= MAX) {
System.out.println("柜台:" + name + " 当前处理的业务号为 " + (index++));
}
}
public static void main(String[] args) {
// 3.创建实例
TicketWindow ticketWindow1 = new TicketWindow("No.1");
// 4.调用start,开启线程
ticketWindow1.start();
TicketWindow ticketWindow2 = new TicketWindow("No.2");
ticketWindow2.start();
TicketWindow ticketWindow3 = new TicketWindow("No.3");
ticketWindow3.start();
TicketWindow ticketWindow4 = new TicketWindow("No.4");
ticketWindow4.start();
}
}
输出结果:
柜台:No.2 当前处理的业务号为 1
...
柜台:No.2 当前处理的业务号为 5
柜台:No.3 当前处理的业务号为 1
...
柜台:No.3 当前处理的业务号为 6
柜台:No.4 当前处理的业务号为 1
柜台:No.1 当前处理的业务号为 1
柜台:No.1 当前处理的业务号为 2
柜台:No.4 当前处理的业务号为 2
...
柜台:No.4 当前处理的业务号为 8
柜台:No.3 当前处理的业务号为 7
柜台:No.2 当前处理的业务号为 6
柜台:No.2 当前处理的业务号为 7
柜台:No.2 当前处理的业务号为 8
柜台:No.3 当前处理的业务号为 8
柜台:No.1 当前处理的业务号为 3
..
柜台:No.1 当前处理的业务号为 8
Thread类的run方法是不能共享的,也就是说A线程不能把B线程的run方法当作自己的执行单元!由于ticketWindow1、ticketWindow2...是不同的实例,index不共享,导致四个线程的index各自从1增至8!
无论TicketWindow被实例化多少次,只需要保证index是唯一的即可,可以使用static去修饰index(只实例化一个)以达到目的:
private static int index = 1;
输出结果:
柜台:No.2 当前处理的业务号为 1
柜台:No.2 当前处理的业务号为 5
柜台:No.2 当前处理的业务号为 6
柜台:No.2 当前处理的业务号为 7
柜台:No.2 当前处理的业务号为 8
柜台:No.4 当前处理的业务号为 3
柜台:No.1 当前处理的业务号为 4
柜台:No.3 当前处理的业务号为 2
通过对index进行static修饰,做到了多线程下共享资源的唯一性。
但是只有一个index共享资源,如果共享资源很多?共享资源要经过一些比较复杂的计算?
static修饰的变量生命周期很长,不可能都使用static修饰!
实现Runnable接口
将线程的控制和业务逻辑的运行彻底分离开来!
Thread类的run方法是不能共享的,也就是说A线程不能把B线程的run方法当作自己的执行单元!而使用Runnable接口则很容易就能实现这一点,使用同一个Runnable的实例构造不同的Thread实例。
/**
* 实现Runnable接口
*/
public class TicketWindowRunnable implements Runnable {
private int index = 1;
private final static int MAX = 8;
/**
* 将Thread中run的代码逻辑抽取到Runnable的一个实现中
*/
@Override
public void run() {
while (index <= MAX) {
System.out.println(Thread.currentThread() + " 正在办理的业务号为 " + (index++));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 将业务逻辑的运行
final TicketWindowRunnable task = new TicketWindowRunnable();
// 与线程的控制彻底分离开来!
Thread thread1 = new Thread(task, "1");
Thread thread2 = new Thread(task, "2");
Thread thread3 = new Thread(task, "3");
Thread thread4 = new Thread(task, "4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
输出结果出现了线程不安全问题:
Thread[3,5,main] 正在办理的业务号为 1
Thread[4,5,main] 正在办理的业务号为 3
Thread[2,5,main] 正在办理的业务号为 2
Thread[1,5,main] 正在办理的业务号为 2
Thread[1,5,main] 正在办理的业务号为 4
Thread[3,5,main] 正在办理的业务号为 5
Thread[4,5,main] 正在办理的业务号为 7
Thread[2,5,main] 正在办理的业务号为 6
Thread[3,5,main] 正在办理的业务号为 8
Thread[2,5,main] 正在办理的业务号为 8
Thread[1,5,main] 正在办理的业务号为 8
Thread[4,5,main] 正在办理的业务号为 8
Thread源码
public class Thread implements Runnable {
/**
* Allocates a new Thread object.
*
* @param target
* the object whose run method is invoked when this thread is started.
* If null, this thread's run method is invoked.
*
* @param name
* the name of the new thread
*/
public Thread(Runnable target, String name) {
init(null, target, name, 0);
}
public synchronized void start() {
/**
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then it will be passed up the call stack */
}
}
}
private native void start0();
/**
* If this thread was constructed using a separate Runnable run object, then that Runnable object's run method is called;
* otherwise, this method does nothing and returns.
* Thread(Runnable target, String name)
* Subclasses of Thread should override this method.
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元则有两种方式,第一种是重写Thread的run方法;第二种是实现Runnable接口的run方法,并且将Runnable实例用作构造Thread的参数。
start()最核心的部分是start0这个本地方法,Thread被构造后的NEW状态,事实上threadStatus这个内部属性为0;不能两次启动Thread,否则就会出现IllegalThreadStateException异常;线程启动后将会被加入到一个ThreadGroup中;一个线程生命周期结束,也就是到了TERMINATED状态,再次调用start方法是不允许的。
Thread采用的模板设计模式
Thread的run()和start()是一个比较典型的模板设计模式:父类编写算法结构代码,子类实现逻辑细节。
public class TemplateMethodThread {
public final void startPrintMessage(String message) { // start()
System.out.println("##########");
runWrapPrint(message); // run()
System.out.println("##########");
}
protected void runWrapPrint(String message) {
System.out.println("runWrapPrint");
}
public static void main(String[] args) {
TemplateMethodThread method1 = new TemplateMethodThread() {
@Override
protected void runWrapPrint(String message) {
System.out.println("method1: Override runWrapPrint");
}
};
method1.startPrintMessage("method1: Hello Thread");
TemplateMethodThread method2 = new TemplateMethodThread() {
@Override
protected void runWrapPrint(String message) {
System.out.println("method2: Override runWrapPrint");
}
};
method2.startPrintMessage("method2: Hello Thread");
}
}
使用匿名内部类与Lambda创建线程
// 使用匿名内部类的方式,实现多线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "新线程创建了!");
}
}).start();
// 使用Lambda表达式,实现多线程
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "新线程创建了!");
}
).start();

浙公网安备 33010602011771号