新手初學Java網絡編程
運行線程
創建Thread的子類
public class ThreadChild extends Thread { @Override public void run() { while (true) { System.out.println("run"); } } }
public class Test { public static void main(String[] args) throws Exception { ThreadChild t = new ThreadChild(); t.start(); } }
創建Thread傳入Runnable接口實現類
public class RunnableImpl implements Runnable { @Override public void run() { while (true) { System.out.println("run"); } } }
public class Test { public static void main(String[] args) throws Exception { Thread t = new Thread(new RunnableImpl()); t.start(); } }
回調
ExecutorService線程池的Submit()可接收Runnable或者Callable.Callable執行結束後有返回值,Runnable執行結束後沒有返回值.可以通過submit的返回對象Future的get方法獲取返回返回值,需要註意的是get方法是一個阻塞方法.若線程沒有執行完畢,則會阻塞get()直到線程執行結束後返回數據.
public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(10); Future<Integer> future = executorService.submit(() -> { TimeUnit.SECONDS.sleep( 3); return 1; }); Future<?> future2 = executorService.submit(() -> { }); Future<String> future3 = executorService.submit(() -> { }, "aaa"); System.out.println(future.get()); System.out.println(future2.get()); System.out.println(future3.get()); }
同步方法
synchronized關鍵字和方法組合使用後,該方法是一個同步方法。同步方法都有一個隱試的鎖,多個線程同時執行該方法時,隻能順序執行,未強到鎖的線程處於阻塞狀態。另外靜態方法和非靜態方法使用的是不同的鎖,非靜態方法的鎖是對象鎖,若兩個線程通過兩個該類的對象調用那麼互不影響。靜態方法是類鎖,即本類的Class對象。同步方法無法指定鎖資源,要麼是this鎖,要麼是Class鎖。
public class Test { public static void main(String[] args) throws Exception { Test test = new Test(); Thread t1 = new Thread(()->{ test.synFunction(); }); Thread t2 = new Thread(()->{ test.synFunction(); }); t1.start(); t2.start(); } public synchronized void synFunction(){ try { System.out.println("-------------"); TimeUnit.SECONDS.sleep(3); }catch (Exception e) {} } }
public class Test { public static void main(String[] args) throws Exception { Thread t1 = new Thread(()->{ Test.synFunction(); }); Thread t2 = new Thread(()->{ Test.synFunction(); }); t1.start(); t2.start(); } public static synchronized void synFunction(){ try { System.out.println("-------------"); TimeUnit.SECONDS.sleep(3); }catch (Exception e) {} } }
同步塊
同步代碼塊可以指定鎖對象,非靜態方法可以指定任意的對象鎖,或者任意的類鎖。但靜態方法隻能使用任意的類鎖。
public class Test { public static void main(String[] args) throws Exception { Test lock = new Test(); Thread t1 = new Thread(() -> { lock.synFunction(); }); Thread t2 = new Thread(() -> { lock.synFunction(); }); t1.start(); t2.start(); } public void synFunction() { synchronized (this) {//對象鎖 try { System.out.println("-------------"); TimeUnit.SECONDS.sleep(3); } catch (Exception e) { } } } }
public class Test { public static void main(String[] args) throws Exception { Test lock1 = new Test(); Test lock2 = new Test(); Thread t1 = new Thread(() -> { lock1.synFunction(lock1); }); Thread t2 = new Thread(() -> { lock2.synFunction(lock2); }); t1.start(); t2.start(); } public void synFunction(Object lock) { synchronized (lock) {//對象鎖 try { System.out.println("-------------"); TimeUnit.SECONDS.sleep(3); } catch (Exception e) { } } } }
public class Test { public static void main(String[] args) throws Exception { Test lock1 = new Test(); Test lock2 = new Test(); Thread t1 = new Thread(() -> { lock1.synFunction(); }); Thread t2 = new Thread(() -> { lock2.synFunction(); }); t1.start(); t2.start(); } public void synFunction() { synchronized (Test.class) {//類鎖 try { System.out.println("-------------"); TimeUnit.SECONDS.sleep(3); } catch (Exception e) { } } } }
public class Test { public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { Test.synFunction(); }); Thread t2 = new Thread(() -> { Test.synFunction(); }); t1.start(); t2.start(); } public static void synFunction() { synchronized (Object.class) {//類鎖 try { System.out.println("-------------"); TimeUnit.SECONDS.sleep(3); } catch (Exception e) { } } } }
死鎖
當多個線程競爭鎖時,可能會導致死鎖。一個線程由於沒有得到一個鎖而阻塞它也不會釋放已經獲取的鎖。方法1獲取鎖的順序是Test.class,Object.class。方法2獲取鎖的順序是Object.class,Test.class。程序會陷入死鎖。
public class Test { public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { Test.synFunction1(); }); Thread t2 = new Thread(() -> { Test.synFunction2(); }); t1.start(); t2.start(); } public static void synFunction1() { synchronized (Test.class) {// 類鎖 try { System.out.println("synFunction1-------------AAAAAAAAAAAAA"); synchronized (Object.class) {// 類鎖 try { System.out.println("synFunction1-------------bbbbbbbbbbbbb"); } catch (Exception e) { } } } catch (Exception e) { } } } public static void synFunction2() { synchronized (Object.class) {// 類鎖 try { System.out.println("synFunction2-------------AAAAAAAAAAAAA"); } catch (Exception e) { } synchronized (Test.class) {// 類鎖 try { System.out.println("synFunction2-------------bbbbbbbbbbbbb"); } catch (Exception e) { } } } } }
優先級
不是所有線程創建時都是均等的,每個線程都有一個優先級,指定從0-10的整數。當多個線程運行時,虛擬機通常隻運行最高優先級的線程,但這並不是一個嚴格的規則。在Java中10是最高優先級,0是最低優先級。默認優先級為5.並不是所有操作系統都支持這11個優先級。例如Windows隻有7個優先級。優先級(1,2)(3,4)(6,7)(8,9)會做同樣的處理。
public class Test { public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { Test.synFunction("aaaaaaaaaaa"); }); Thread t2 = new Thread(() -> { Test.synFunction("bbbbbbbbbb"); }); t2.setPriority(9); t1.setPriority(1); t1.start(); t2.start(); } public static void synFunction(String name) { System.out.println(name+"--------------"); } }
暫停
為瞭能讓其他線程有機會運行,一個線程有8中方式可以暫停或者指示它準備暫停。
可以對IO阻塞
要讓網絡程序中的線程自動放棄CPU控制權,最常見的方式是IO阻塞。由於CPU比網絡和磁盤快的多,網絡程序經常會在等待數據從網絡到達或向網絡發送數據時阻塞。即使隻阻塞幾毫秒,這一點時間也足夠其他線程用來完成重要的任務。
可以對同步對象阻塞
線程在進入一個同步方法或代碼塊時也會阻塞。如果這個線程沒有所同步對象的鎖,而其他線程擁有這個鎖,這個線程就會暫停,直到鎖被釋放為止。如果這個鎖永遠不會釋放,那麼這個線程會永久停止。
可以放棄
調用Thread.yield()靜態方法可以做到顯試的放棄線程控制權,這將通知虛擬機,可以優先執行其他線程。放棄並不會釋放這個線程擁有的鎖,因此在理想情況下被放棄的線程不要做任何的同步。如果等待運行的其他線程都是因為這個線程所擁有的同步資源而阻塞,那麼這些線程將不能運行。實際上,控制權將回到唯一可以運行的線程,即剛剛放棄的這個線程,這很大程度上失去瞭放棄的意義。
public class Test { public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { Test.synFunction("aaaaaaaaaaa"); }); Thread t2 = new Thread(() -> { Test.synFunction2("bbbbbbbbbb"); }); t1.start(); t2.start(); } public static void synFunction(String name) { for (int i = 0;; i++) { Thread.yield(); System.out.println(name + "--------------" + i); } } public static void synFunction2(String name) { for (int i = 0;; i++) System.out.println(name + "--------------"+i); } }
可以休眠
休眠是更有利的放棄方式。放棄隻是表示線程願意暫停,讓其他有相同優先級的線程有機會運行,而進入休眠的線程有所不同,不管有沒有其他線程準備運行,休眠線程都會暫停。這樣一來,不隻是其他有相同優先級的線程會得到機會,還會給較低優先級的線程運行機會。不過進入休眠的線程仍然擁有它已經獲得得所有鎖。因此其他需要相同鎖得線程依然會阻塞,要避免在同步方法或塊內讓線程休眠。
public class Test { public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { Test.synFunction("aaaaaaaaaaa"); }); Thread t2 = new Thread(() -> { Test.synFunction2("bbbbbbbbbb"); }); t1.start(); t2.start(); } public synchronized static void synFunction(String name) { try { System.out.println(name); Thread.sleep(1000 * 10); } catch (Exception e) { } } public synchronized static void synFunction2(String name) { System.out.println(name); } }
package com.datang.bingxiang.demo; import java.util.concurrent.TimeUnit; public class Test { public static void main(String[] args) throws Exception { Thread t1 = new Thread(() -> { Test.synFunction("aaaaaaaaaaa"); }); Thread t2 = new Thread(() -> { Test.synFunction2("bbbbbbbbbb"); }); t1.start(); t2.start(); } public synchronized static void synFunction(String name) { try { System.out.println(name); Thread.sleep(1000 * 3, 999999);//3000毫秒+999999毫秒(第二個參數不能超過999999,也就是不能超過1毫秒) } catch (Exception e) { System.out.println(e); } } public synchronized static void synFunction2(String name) { System.out.println(name); } }
可以連接另一個線程
連接線程(既調用join()方法得線程A)等待被連接的線程(join()方法所屬的線程對象B)執行完畢後才會執行。在A線程調用B線程的join()A線程會等待B線程執行結束後才會繼續執行。要註意的是A線程並不會釋放已經獲取的鎖資源。
public class Test { public static void main(String[] args) throws Exception { Thread t2 = new Thread(() -> { Test.synFunction2("bbbbbbbbbb"); }); Thread t1 = new Thread(() -> { Test.synFunction("aaaaaaaaaaa",t2); }); t1.start(); t2.start(); } public static void synFunction(String name,Thread t) { try { t.join(); //t.join(1000); //t.join(1000,999999); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 5; i++) { System.out.println(name); } } public static void synFunction2(String name) { try { Thread.sleep(1000*3); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 5; i++) { System.out.println(name); } } }
可以等待一個對象
線程可以等待一個它鎖定的對象.在等待時,它會釋放這個對象的鎖並暫停,直到它得到其他線程的通知.另一個線程以某種方式修改這個對象,通知等待對象的線程,然後繼續執行.等待會暫停執行,直到一個對象或資源到達某種狀態.實際上,要等待某個特定的對象,希望暫停的線程首先必須使用synchronized獲得這個對象的鎖,然後調用這個對象的三個重載wait()方法之一.
public class Demo { public static void main(String[] args) throws Exception { Demo d = new Demo(); Thread t1 = new Thread(() -> { d.a(); }); Thread t2 = new Thread(() -> { d.b(); }); t1.start(); t2.start(); } boolean flag = true; public synchronized void a() { try { while (true) { if (flag) { System.out.println("執行AAAAAAAAAAA"); flag = false; } else { wait();//等待,不限制等待時間 //wait(1000);//等待指定的時間,沒有被notify喚醒也可以自己退出等待 //wait(1000,999999);//毫秒+納秒的等待時間 } notify(); } } catch (Exception e) { } } public synchronized void b() { try { while (true) { if (!flag) { System.out.println("執行BBBBBBBBBBB"); flag = true; } else { wait(); } notify(); } } catch (Exception e) { } } }
可以結束
線程要以合理的方式放棄CPU控制權,一種方式是結束.當run()方法返回線程時,線程將撤銷,其他線程可以接管CPU.在網絡應用程序中,包裝一個阻塞操作的線程往往會這麼做,例如線程從服務器下載一個文件,這樣應用程序的其他部分就不會被阻塞.另一方面,如果run()方法太簡單,總是很快結束,而不會紫色,那就存在一個很實際的問題:到底有沒有必要生成一個線程.虛擬機在建立和撤銷線程時會有很大的開銷.如果線程會在極端的時間內結束,那麼使用一次簡單的方法調用而不是單獨的線程可能會結束的更快.
可以被更高優先級線程搶占
Thread的setPriority(int priority)
設置線程的優先級.
總結
本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- 淺談Java鎖的膨脹過程以及一致性哈希對鎖膨脹的影響
- 新手瞭解java 多線程基礎知識
- Java多線程Thread類的使用及註意事項
- 一篇文章讓你徹底瞭解Java可重入鎖和不可重入鎖
- Java項目有中多個線程如何查找死鎖