Java實現多線程的兩種方式分別是繼承Thread類和實現Runnable接口。
- 繼承Thread類
class MyThread extends Thread{
@Override
public void run(){
for(int i=0;i<7;i++){
System.out.println("MyThread:"+Thread.currentThread().getName()+" 次數:"+i);
}
}
}
- 實現Runnable接口
class MyTask implements Runnable{
@Override
public void run(){
for(int i=0;i<7;i++){
System.out.println("MyTask:"+Thread.currentThread().getName()+" 次數:"+i);
}
}
}
- 測試
public class Test {
public static void main(String [] args){
MyThread myThread = new MyThread();
myThread.start();
MyTask task = new MyTask();
Thread thread = new Thread(task);
thread.start();
}
}
MyThread:Thread-0 次數:0
MyThread:Thread-0 次數:1
MyThread:Thread-0 次數:2
MyThread:Thread-0 次數:3
MyThread:Thread-0 次數:4
MyThread:Thread-0 次數:5
MyTask:Thread-1 次數:0
MyThread:Thread-0 次數:6
MyTask:Thread-1 次數:1
MyTask:Thread-1 次數:2
MyTask:Thread-1 次數:3
MyTask:Thread-1 次數:4
MyTask:Thread-1 次數:5
MyTask:Thread-1 次數:6
程序會在不同的線程之間切換,每次的輸出結果并不相同。
關鍵點1:啟動線程使用start()方法,使用run()方法并不會啟動新線程。
public class Test {
public static void main(String [] args){
MyThread myThread = new MyThread();
myThread.run();
MyTask task = new MyTask();
Thread thread = new Thread(task);
thread.run();
}
}
MyThread:main 次數:0
MyThread:main 次數:1
MyThread:main 次數:2
MyThread:main 次數:3
MyThread:main 次數:4
MyThread:main 次數:5
MyThread:main 次數:6
MyTask:main 次數:0
MyTask:main 次數:1
MyTask:main 次數:2
MyTask:main 次數:3
MyTask:main 次數:4
MyTask:main 次數:5
MyTask:main 次數:6
start() : 通過調用start0()啟動一個新線程,新線程會執(zhí)行相應的run()方法。start()不能被重復調用。
run(): run()和普通的成員方法一樣可重復調用。單獨調用run()會在當前線程中執(zhí)行run(),并不會啟動新線程!
相關源碼:
public synchronized void start() {
if (threadStatus != 0) //start()方法不能被重復調用,否則會拋出異常
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();
關鍵點2:如何選擇Thread與Runnable.
其實Thread同樣實現了Runnable接口。
class Thread implements Runnable {
//…
public void run() {
if (target != null) {
target.run();
}
}
}
Thead的作用是啟動新的線程,而Runnable的作用表示執(zhí)行的任務。 (很好的體現了接口的原則:功能抽離;)
繼承Thead類表示我們需要一種特殊的Thead;而實現Runnable接口表示我們需要執(zhí)行某種任務;
Thread與Runnable的區(qū)別:
- 由于Java使用單繼承,多接口實現原則,所以通過Runnable接口實現多線程更具靈活性;
- Runnable對象可以被多個線程共享,更適合于多個線程處理同一資源的情況。
(正確共享資源仍然需要加鎖)
示例(賣票問題):
- Thread
class MyThread extends Thread{
private int tickets =5;
@Override
public void run() {
while (tickets>0) {
tickets--;
System.out.println(Thread.currentThread().getName()+"賣了一張票,剩余票數:"+tickets);
}
}
}```
``` java
public class Test {
public static void main(String [] args){
MyThread myThread1 = new MyThread();
myThread1.start();
MyThread myThread2 = new MyThread();
myThread2.start();
}
}
Thread-0賣了一張票,剩余票數:4
Thread-1賣了一張票,剩余票數:4
Thread-1賣了一張票,剩余票數:3
Thread-0賣了一張票,剩余票數:3
Thread-1賣了一張票,剩余票數:2
Thread-1賣了一張票,剩余票數:1
Thread-1賣了一張票,剩余票數:0
Thread-0賣了一張票,剩余票數:2
Thread-0賣了一張票,剩余票數:1
Thread-0賣了一張票,剩余票數:0
MyThread myThread2 = new MyThread();```
因為每次都創(chuàng)建了新的MyThread對象,內存中有兩份tickets,所以最后結果是賣了10張票;
1. Runnable
``` java
class MyRunnable implements Runnable{
private int tickets =5;
@Override
public void run() {
while (tickets>0) {
tickets--;
System.out.println(Thread.currentThread().getName()+"賣了一張票,剩余票數:"+tickets);
}
}
}
public class Test {
public static void main(String [] args){
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
Thread-0賣了一張票,剩余票數:4
Thread-1賣了一張票,剩余票數:3
Thread-0賣了一張票,剩余票數:2
Thread-0賣了一張票,剩余票數:1
Thread-0賣了一張票,剩余票數:0
MyRunnable對象只有一個,多線程共享同一對象,內存中只有1份tickets,賣票結果好像是正確的。
- synchronized加鎖
class MyRunnable implements Runnable{
private int tickets =5;
@Override
public void run() {
while (tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println(Thread.currentThread().getName()+"賣了一張票,剩余票數:"+tickets);
}
}
}
Thread-0賣了一張票,剩余票數:4
Thread-1賣了一張票,剩余票數:4
Thread-1賣了一張票,剩余票數:3
Thread-0賣了一張票,剩余票數:2
Thread-0賣了一張票,剩余票數:0
Thread-1賣了一張票,剩余票數:0
顯然一共賣了6張票是不正確的,所以僅僅實現Runnable接口是無法進行資源共享的,如果需要共享還是需要加入synchronized關鍵字。
class MyRunnable implements Runnable{
private int tickets =5;
@Override
public void run() {
synchronized(this){
while (tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println(Thread.currentThread().getName()+"賣了一張票,剩余票數:"+tickets);
}
}
}
}
Thread-0賣了一張票,剩余票數:4
Thread-0賣了一張票,剩余票數:3
Thread-0賣了一張票,剩余票數:2
Thread-0賣了一張票,剩余票數:1
Thread-0賣了一張票,剩余票數:0
[2015-08-20]