通過幾天的認真閱讀,發現這是一本難得一見的好書,為了加深鞏固學習成功,我打算將書中的例子全部自己實現一遍,特此記錄下來也方便其他朋友學習。
第一章,java語言的線程
單線程程序:打印10000次good字符串
public class SingleThreadSample {
public static void main(String[] args) {
for(int i=0; i< 10000; i++){
System.out.print("good!");
}
}
}
嚴格的說並不是只有一個線程在操作,還有其他的線程在非java處理系統上運行,比如gc,gui相關的線程等。
第一個多線程程序:實現了交替打印good和nice的功能
public class MyThreadTest {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
for (int i = 0; i < 10000; i++) {
System.out.println("good!");
}
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("nice!");
}
}
}
這里加入一個並發和並行概念的區別,並發是concurrent,是指多個線程在同一個cpu上切換進行執行。並行是parallel,指多個線程是在各自的cpu上同時執行的。
我們增強一下剛才的多線程例子,把打印的字符串變成通過參數傳遞。
public class MyThread2Test {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2("good!");
MyThread2 t2 = new MyThread2("nice!");
t1.start();
t2.start();
}
}
class MyThread2 extends Thread {
private String message;
public MyThread2(String message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(message);
}
}
}
剛才是通過集成Thread抽象類的子類方式實現多線程,另外還可以通過Runnable接口的方式,例子如下:
public class MyThread3Test {
public static void main(String[] args) {
MyThread3 t1 = new MyThread3("good!");
MyThread3 t2 = new MyThread3("nice!");
new Thread(t1).start();
new Thread(t2).start();
}
}
class MyThread3 implements Runnable {
private String message;
public MyThread3(String message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(message);
}
}
}
啟動和執行多線程已經說完了,那么該說說如何讓線程休息休息。
第一種方式是通過Thread.sleep(ms)方法,需要注意的是這個方法有一個重載Thread.sleep(ms,ns),可以把停止的時間控制到納秒級。
另外Thread.yield()方法也可以在循環體中使用,表示如果沒有cpu時間則將當前線程切換到其他子線程,可以簡單理解成Thread.sleep(0);
不過Thread.sleep會拋出InterruptedException異常,Thread.yield不會。
下面在說說線程互斥,還是剛才的例子,如果我打算讓程序執行10000次打印的過程是一個整體,執行過程中不允許切換到其他子線程,那么就需要使用Synchronzed關鍵字。
public class MyThreadMutualTest {
public static void main(String[] args) {
PrintMessage pmsg = new PrintMessage();
new MyThreadMutual(pmsg,"good").start();
new MyThreadMutual(pmsg,"nice").start();
}
}
class MyThreadMutual extends Thread {
private String message;
private PrintMessage printMessage;
public MyThreadMutual(PrintMessage printMessage,String message) {
this.printMessage = printMessage;
this.message=message;
}
@Override
public void run() {
printMessage.show(message);
}
}
class PrintMessage {
public synchronized void show(String msg) {
for (int i = 0; i < 10000; i++) {
System.out.println(msg);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
接下來講一下線程的協調,主要有三個方法:wait() notify() notifyAll()
這三個方法都是object類的方法,可以理解成一個休息室,調用obj.wait()方法表示當前執行的線程進入休息室,休息室里可能會有多個線程,如果沒有其他線程給休息室發消息通知它們可以出去了,這些線程就會一直在里面休息。
當調用obj.notify()方法,表示休息室中可以有一個線程退出,如果里面有多個線程,會隨機選取一個,而obj.notifyAll()表示所有的線程都可以退出休息室。
現在把上面的例子修改一下,想打印10次good再打印10次nice,這樣交替執行。
public class ThreadMutualTest2 {
public static void main(String[] args) {
Object obj = new Object();
MyThreadMutualA a = new MyThreadMutualA("nice", obj);
MyThreadMutualB b = new MyThreadMutualB("good", obj);
a.start();
b.start();
}
}
class MyThreadMutualA extends Thread {
private Object obj;
private String message;
public MyThreadMutualA(String message, Object obj) {
this.message=message;
this.obj=obj;
}
@Override
public void run() {
synchronized(obj){
for(int i=1; i<100; i++){
System.out.println(message);
if(i%5==0){
obj.notify();
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
obj.notify();
}
}
}
class MyThreadMutualB extends Thread {
private Object obj;
private String message;
public MyThreadMutualB(String message, Object obj) {
this.message=message;
this.obj=obj;
}
@Override
public synchronized void run() {
synchronized(obj){
for(int i=1; i<100; i++){
System.out.println(message);
if(i%5==0){
obj.notify();
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
obj.notify();
}
}
}
這段代碼廢了好大勁啊,同步互斥是多線程最復雜的最核心的部分了。
