Java線程學習

[size=large][b]從線程中取得信息[/b][/size]
[b]輪詢 [/b] 主程序無限循環,從子線程取得返回值,直到子線程執行完畢(返回值不爲0)
public class ReturnThread extends Thread {
private int time;
private int result;

public ReturnThread(int time) {
this.time = time;
}

public void run() {
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
result = time;
}

public int getResult() {
return result;
}
}

public class ReturnThreadShell {

public static void main(String[] args) {
Random r = new Random(47);
ReturnThread[] threads = new ReturnThread[5];
for (int i = 0; i < 5; i++) {
int time = r.nextInt(10);
ReturnThread thread = new ReturnThread(time);
threads[i] = thread;
thread.start();
}
for (int i = 0; i < threads.length; i++) {
while (true) {
int rslt = threads[i].getResult();
if (rslt != 0) {
System.out.println("thread " + i + "
is finish. return value is " + rslt);
break;
}
}
}
}
}
//thread 0 is finish. return value is 8
//thread 1 is finish. return value is 5
//thread 2 is finish. return value is 3
//thread 3 is finish. return value is 1
//thread 4 is finish. return value is 1


[b]回調[/b] 子線程執行完成後,調用主線程的方法
public class CallbackThread extends Thread {
private int time;
private CallbackThreadShell callback;

public CallbackThread(int time, CallbackThreadShell callback) {
this.time = time;
this.callback = callback;
}

public void run() {
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
callback.receiveResult(time, callback.threadId);
}
}

public class CallbackThreadShell {
private int time;
protected int threadId;

public CallbackThreadShell(int time, int threadId) {
this.time = time;
this.threadId = threadId;
}

public void runThread() {
CallbackThread thread = new CallbackThread(time, this);
thread.start();
}

public void receiveResult(int result, int threadId) {
System.out.println("thread " + threadId
+ " is finish. return value is " + result);
}

public static void main(String[] args) {
Random r = new Random(47);
for (int i = 0; i < 5; i++) {
CallbackThreadShell shell = new CallbackThreadShell(r.nextInt(10), i);
shell.runThread();
}
}
}
//thread 3 is finish. return value is 1
//thread 4 is finish. return value is 1
//thread 2 is finish. return value is 3
//thread 1 is finish. return value is 5
//thread 0 is finish. return value is 8


[size=large][b]同步[/b] [/size]
[b]同步塊[/b] java無法阻止其他所有線程使用共享資源的方法,他只能防止對同一對象同步的其他線程使用該共享資源
[b]同步方法[/b]

[b]同步的替代方法[/b]
1 使用局部變量代替類變量
2 簡單類型的方法參數是安全的,因爲java通過值傳遞不是引用傳遞參數,對象類型的方法參數,如果是final的,也是線程安全的
3 將非線程安全的類,作爲一個線程安全的類的私有字段


[size=large][b]死鎖[/b][/size]
防止死鎖要避免不必要的線程同步!!


[size=large][b]線程調度[/b][/size]
[b]搶佔與協作[/b] 搶佔式線程調度器確定線程公平的享用CPU時間,然後暫停此線程,將CPU控制權交給另外的線程。(飢餓問題很難發現)
協作式線程調度器會在CPU控制權交給其他線程前,等待運行中的線程自己暫停。

爲了有利於其他線程,一個線程有以下方式可以暫停或指示準備暫停
[b]阻塞[/b] I/O時阻塞、同步其他對象是阻塞。當線程必須停下來,等待他沒有的資源,就發生了阻塞。此時線程不會釋放任何線程已經擁有的鎖。

[b]放棄 [/b] Thread.yield()。線程願意暫停,讓其他同等優先級的線程有機會運行

[b]休眠 [/b] Thread.sleep()。線程不管有沒有其他線程準備運行都會暫停
調用休眠線程的interrupt()方法,可以喚醒該線程,這是線程與Thread對象之間的重要區別之一(線程休眠中,仍然可以調用方法與之交互)喚醒會讓休眠線程得到一個InterruptedException
一個通過InterruptedException結束線程的例子

public void run(){
while(true){
try{ Thread.sleep(300000);
}catch(InterruptedException e){ break;}}}


[b]連接線程 [/b] Thread.join()。一個線程需要另一個線程的結果,即主線程(調用join()方法的線程)等待被連接的線程(join()方法被調用的線程)結束.

[b]等待一個對象[/b] Object.wait()。等待用於暫停執行,直到一個對象或資源達到某種狀態,連接則用於暫停執行,知道一個線程結束。在等待時,他會釋放此對象的鎖並暫停(但不是他擁有的任何其他對象的鎖),直到得到其他線程通知notify()或時間到期、線程被中斷interrupt()。
一旦等待線程得到通知,他就試圖重新獲得所等待對象的鎖,如果成功,就繼續執行緊接着wait()調用後的語句,如果失敗,他就會阻塞與此對象,直到可以得到鎖。
while (pool.isEmpty()) {
try {
pool.wait();
// 收到通知時,停止等待,執行之後的內容
// 必須pool.isEmpty(),因爲我們不知道處理完成後,pool中是否仍有內容
} catch (InterruptedException ex) {}
}
// 取得一個連接,處理這一項。。。
connection = (Socket) pool.remove(0);

synchronized (pool) {
pool.add(pool.size(), request);
pool.notifyAll();
}

[b]基於優先級的搶佔[/b] setPriority()方法

[b]結束[/b] 當run()方法返回時,線程將銷燬,其他線程就可以接管CPU。


[size=large][b]線程池[/b][/size]
實現方法
1 第一次創建池時,分配固定數量的線程,當池爲空時,所有線程都在等待,當向池添加一項任務時,所有等待的線程都得到通知。當一個線程結束其分配的任務時,它再回到池中等待新任務。
2 將線程本身放在池中,讓主程序從池中取出線程,爲其分配任務。每個線程結束後返回池中。


一個例子,多線程壓縮文件

public class GZipThread extends Thread {

private List pool;
private static int filesCompressed = 0;

public GZipThread(List pool) {
this.pool = pool;
}

private static synchronized void incrementFilesCompressed() {
filesCompressed++;
System.out.println(filesCompressed);
}

public void run() {
while (filesCompressed != GZipAllFiles.getNumberOfFilesToBeCompressed()) {
File input = null;
synchronized (pool) {
while (pool.isEmpty()) {
if (filesCompressed == GZipAllFiles.getNumberOfFilesToBeCompressed()) {
System.out.println("Thread ending");
return;
}
try {
pool.wait();
} catch (InterruptedException ex) {
}
}
input = (File) pool.remove(pool.size() - 1);
incrementFilesCompressed();
}
// don't compress an already compressed file
if (!input.getName().endsWith(".gz")) {
try {
InputStream in = new FileInputStream(input);
in = new BufferedInputStream(in);
File output = new File(input.getParent(), input.getName() + ".gz");
if (!output.exists()) { // Don't overwrite an existing file
OutputStream out = new FileOutputStream(output);
out = new GZIPOutputStream(out);
out = new BufferedOutputStream(out);
int b;
while ((b = in.read()) != -1)
out.write(b);
out.flush();
out.close();
in.close();
}
} catch (IOException ex) {
System.err.println(ex);
}
} // end if
} // end while
} // end run
} // end ZipThread

public class GZipAllFiles {

public final static int THREAD_COUNT = 4;
private static int filesToBeCompressed = -1;

public static void main(String[] args) {
Vector pool = new Vector();
GZipThread[] threads = new GZipThread[THREAD_COUNT];
for (int i = 0; i < threads.length; i++) {
threads[i] = new GZipThread(pool);
threads[i].start();
}
int totalFiles = 0;
for (int i = 0; i < args.length; i++) {
File f = new File(args[i]);
if (f.exists()) {
if (f.isDirectory()) {
File[] files = f.listFiles();
for (int j = 0; j < files.length; j++) {
// 不遞歸文件夾
if (!files[j].isDirectory()) {
totalFiles++;
synchronized (pool) {
pool.add(0, files[j]);
pool.notifyAll();
}
}
}
} else {
totalFiles++;
synchronized (pool) {
pool.add(0, f);
pool.notifyAll();
}
}
} // end if
} // end for
filesToBeCompressed = totalFiles;
System.out.println("totalFiles " + filesToBeCompressed);
// 讓等待線程知道,沒有更多的文件會加到池中了
// 如果需要壓縮文件很少,有可能線程啓動了,但沒有需要壓縮的文件
for (int i = 0; i < threads.length; i++) {
threads[i].interrupt();
}
}

public static int getNumberOfFilesToBeCompressed() {
return filesToBeCompressed;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章