兩種實作方式繼承Thread類或者實作Runnable介面
使用實作Runnable介面和繼承Thread類這兩種開辟新執行緒的方法的選擇應該優先選擇實作Runnable介面這種方式去開辟一個新的執行緒,因為介面的實作可以實作多個,而類的繼承只能是單繼承,因此在開辟新執行緒時能夠使用Runnable介面就盡量不要使用從Thread類繼承的方式來開辟新的執行緒,
1 public class mainTest { 2 public static void main(String[] args) { 3 System.out.println("main Start....."); 4 //Thread有Runnable引數的構造方法 5 new Thread(new Runnable() { 6 public void run() { 7 for (int i = 0; i < 10; i++) { 8 System.out.println(Thread.currentThread().getName()+"--->["+i+"]"); 9 } 10 } 11 }).start(); 12 //繼承Thread類 13 new Mythread().start(); 14 System.out.println("main End...."); 15 } 16 17 static class Mythread extends Thread{ 18 @Override 19 public void run() { 20 for (int i = 0; i < 10; i++) { 21 System.out.println(Thread.currentThread().getName()+"===>["+i+"]"); 22 } 23 } 24 } 25 }
Thread的常用方法

執行緒生命周期

執行緒同步synchronized
方法一:同步代碼塊
1 synchronized(同步監視器){ 2 需要被同步的代碼 3 }
操作的共享資料即為需要被同步的代碼
同步監視器又名鎖,任意物件都可作為鎖,為解決執行緒安全問題當多個執行緒操作共享資料時這些執行緒必須公用一把鎖
通過實作Runnable可以用this作為鎖,繼承Thread類可以用子類的Class作為鎖
1 public class mainTest { 2 public static void main(String[] args) { 3 System.out.println("main Start....."); 4 //只有一個票池,作為成員變數的鎖也只有一個 5 ticketPool pool = new ticketPool(); 6 Thread t1 = new Thread(pool,"t1"); 7 Thread t2 = new Thread(pool,"t2"); 8 Thread t3 = new Thread(pool,"t3"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 13 //有多個票池,共享資料和鎖應該用static修飾 14 new ticketPool02().start(); 15 new ticketPool02().start(); 16 new ticketPool02().start(); 17 System.out.println("main End...."); 18 } 19 20 static class ticketPool implements Runnable{ 21 //共享資料 22 private int num = 20; 23 //鎖 24 Object obj = new Object(); 25 public void run() { 26 while(true){ 27 synchronized(this){ 28 if (num > 0){ 29 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 30 num --; 31 try { 32 Thread.sleep(100); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } finally { 36 } 37 } 38 } 39 } 40 } 41 } 42 43 static class ticketPool02 extends Thread{ 44 //共享資料 45 private static int num = 20; 46 //鎖 47 private static Object obj = new Object(); 48 @Override 49 public void run() { 50 while (true){ 51 synchronized(ticketPool02.class){ 52 if(num > 0){ 53 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 54 num --; 55 try { 56 Thread.sleep(100); 57 } catch (InterruptedException e) { 58 e.printStackTrace(); 59 } finally { 60 } 61 } 62 } 63 } 64 } 65 } 66 }
方法二:同步方法
仍然有鎖,只是不需要自己顯式地宣告
非靜態同步方法的鎖是this
靜態同步方法的鎖是當前類的Class
1 public class mainTest { 2 public static void main(String[] args) { 3 System.out.println("main Start....."); 4 //只有一個票池,作為成員變數的鎖也只有一個 5 ticketPool pool = new ticketPool(); 6 Thread t1 = new Thread(pool,"t1"); 7 Thread t2 = new Thread(pool,"t2"); 8 Thread t3 = new Thread(pool,"t3"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 13 //有多個票池,共享資料和鎖應該用static修飾 14 new ticketPool02().start(); 15 new ticketPool02().start(); 16 new ticketPool02().start(); 17 System.out.println("main End...."); 18 } 19 20 static class ticketPool implements Runnable{ 21 //共享資料 22 private int num = 20; 23 24 public void run() { 25 while(true){ 26 synMethod(); 27 } 28 } 29 30 //同步方法 31 public synchronized void synMethod(){//鎖默認是this 32 if (num > 0){ 33 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 34 num --; 35 try { 36 Thread.sleep(100); 37 } catch (InterruptedException e) { 38 e.printStackTrace(); 39 } finally { 40 } 41 } 42 } 43 } 44 45 static class ticketPool02 extends Thread{ 46 //共享資料 47 private static int num = 20; 48 49 @Override 50 public void run() { 51 while (true){ 52 synMethod(); 53 } 54 } 55 56 //因為有static修飾所以鎖默認是當前類的Class即ticketPool02.class 57 public static synchronized void synMethod(){ 58 if(num > 0){ 59 System.out.println(Thread.currentThread().getName()+"--->["+num+"]"); 60 num --; 61 try { 62 Thread.sleep(100); 63 } catch (InterruptedException e) { 64 e.printStackTrace(); 65 } finally { 66 } 67 } 68 } 69 } 70 }
解決懶漢式單例模式的執行緒安全問題
1 @Data 2 class Student{ 3 private String name; 4 private Integer age; 5 private String studentNo; 6 7 private Student(){} 8 private static Student student; 9 /*1 10 public static synchronized Student getInstance(){ 11 if(student == null){ 12 student = new Student(); 13 } 14 return student; 15 }*/ 16 17 /*2、和上面方法1一樣,都有效率低的弊端,當前面的執行緒實體化物件后后面的執行緒仍要等待獲得鎖 18 public static Student getInstance(){ 19 synchronized (Student.class) { 20 if(student == null){ 21 student = new Student(); 22 } 23 return student; 24 } 25 }*/ 26 27 //3、效率提高了,前面的執行緒實體化后,后面的執行緒就不用排隊等鎖了 28 public static Student getInstance(){ 29 if(student == null){ 30 synchronized (Student.class) { 31 if(student == null){ 32 student = new Student(); 33 } 34 } 35 } 36 return student; 37 } 38 }
死鎖:不同執行緒分別占用對方執行緒需要的同步資源不放棄,都在等待對方釋放自己需要的同步資源,死鎖不會報例外也沒有提示,相關執行緒都將處于阻塞狀態,無法繼續
開發中盡量減少同步資源的定義
盡量避免嵌套同步
應用專門的演算法,
1 public class test02 { 2 public static void main(String[] args) { 3 4 final StringBuffer s1 = new StringBuffer(); 5 final StringBuffer s2 = new StringBuffer(); 6 7 new Thread(){ 8 @Override 9 public void run() { 10 synchronized (s1){ 11 s1.append("A"); 12 try { 13 Thread.sleep(100); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 synchronized(s2){ 18 s2.append("B"); 19 System.out.println(s1.toString()+" "+s2.toString()); 20 } 21 } 22 } 23 }.start(); 24 25 new Thread(new Runnable() { 26 public void run() { 27 synchronized (s2){ 28 s2.append("F"); 29 try { 30 Thread.sleep(100); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 synchronized(s1){ 35 s1.append("G"); 36 System.out.println(s1.toString()+" "+s2.toString()); 37 } 38 } 39 } 40 }).start(); 41 } 42 }
Lock介面解決執行緒安全問題,ReentrantLock
1 public class test02 { 2 public static void main(String[] args) { 3 Runnable myRun = new MyRun(); 4 new Thread(myRun).start(); 5 new Thread(myRun).start(); 6 new Thread(myRun).start(); 7 } 8 9 static class MyRun implements Runnable{ 10 private Integer num = 20; 11 private ReentrantLock lock = new ReentrantLock(); 12 public void run() { 13 while (true){ 14 try{ 15 //呼叫lock() 16 lock.lock(); 17 if (num > 0){ 18 System.out.println(Thread.currentThread().getName()+"["+num+"]"); 19 num --; 20 } 21 }finally { 22 //解鎖 23 lock.unlock(); 24 } 25 } 26 } 27 } 28 }
Lock與synchronized異同
同:都是為了解決執行緒安全問題
異:synchronized,在執行完相應的同步代碼后會自動釋放鎖
Lock,在執行到需要同步的代碼時需要手動的呼叫lock()和unlock()方法加鎖和解鎖;Lock只有代碼塊鎖沒有方法鎖;使用Lock,JVM會用較少的時間調度執行緒,性能更好;區別于synchronized的隱式鎖,Lock是顯式鎖;Lock有更好的擴展性,提供更多子類,
執行緒通信(只能用在同步代碼塊或同步方法里即有synchronized修飾,呼叫者是鎖,這三個方法是定義在Object類里的即任意物件都有這三個方法)
wait();使當前執行緒進入阻塞態,并釋放同步鎖給其他執行緒,
notify();喚醒被wait的一個執行緒,如果有多個wait的執行緒則喚醒優先級高的那個執行緒,
notifyAll();喚醒所有被wait的執行緒,
1 public class test02 { 2 public static void main(String[] args) { 3 Runnable myRun = new MyRun(); 4 new Thread(myRun).start(); 5 new Thread(myRun).start(); 6 new Thread(myRun).start(); 7 } 8 9 static class MyRun implements Runnable{ 10 private Integer num = 20; 11 private ReentrantLock lock = new ReentrantLock(); 12 public void run() { 13 while (true){ 14 synchronized(this){ 15 notify(); //this.notify(); 呼叫者是鎖,與鎖保持一致 16 if (num > 0){ 17 System.out.println(Thread.currentThread().getName()+"["+num+"]"); 18 num --; 19 } 20 try { 21 wait(); //this.wait(); 呼叫者是鎖,與鎖保持一致 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 } 27 } 28 } 29 }
sleep()與wait()的異同:
同:都會使執行緒進入阻塞態,
異:sleep()是定義在Thread類里的方法,wait()是定義在Object類里的方法
sleep()可以隨時呼叫,wait()只能在同步代碼塊或同步方法中呼叫
在同步代碼塊或同步方法中sleep()不會釋放同步鎖,wait()會釋放同步鎖,
生產者消費者模式
1 public class test02 { 2 public static void main(String[] args) { 3 SyncStack stack = new SyncStack(); 4 Runnable p=new Producer(stack); 5 Runnable c = new Consumer(stack); 6 Thread p1 = new Thread(p); 7 Thread c1 = new Thread(c); 8 9 p1.start(); 10 c1.start(); 11 12 } 13 } 14 15 class SyncStack{ //支持多執行緒同步操作的堆疊的實作 16 private int index = 0; 17 private char []data = https://www.cnblogs.com/zyb-mini/p/new char[6]; 18 public synchronized void push(char c){ 19 if(index == data.length){ 20 try{ 21 this.wait(); 22 }catch(InterruptedException e){} 23 } 24 this.notify(); 25 data[index] = c; 26 index++; 27 } 28 public synchronized char pop(){ 29 if(index ==0){ 30 try{ 31 this.wait(); 32 }catch(InterruptedException e){} 33 } 34 this.notify(); 35 index--; 36 return data[index]; 37 } 38 } 39 40 41 class Producer implements Runnable{ 42 SyncStack stack; 43 public Producer(SyncStack s){ 44 stack = s; 45 } 46 public void run(){ 47 for(int i=0; i<20; i++){ 48 char c =(char)(Math.random()*26+'A'); 49 stack.push(c); 50 System.out.println("produced:"+c); 51 try{ 52 Thread.sleep((int)(Math.random()*1000)); 53 }catch(InterruptedException e){ 54 } 55 } 56 } 57 } 58 59 60 class Consumer implements Runnable{ 61 SyncStack stack; 62 public Consumer(SyncStack s){ 63 stack = s; 64 } 65 public void run(){ 66 for(int i=0;i<20;i++){ 67 char c = stack.pop(); 68 System.out.println("消費:"+c); 69 try{ 70 Thread.sleep((int)(Math.random()*1000)); 71 }catch(InterruptedException e){ 72 } 73 } 74 } 75 }
JDK5提供了創建執行緒的新方法
1、實作Callable介面,其中的call()方法類似Runnable介面里的run()方法,call()方法可以回傳值、拋例外、支持泛型,
1 public class test02 { 2 public static void main(String[] args) { 3 4 Callable call = new MyCall(); 5 FutureTask future = new FutureTask(call); 6 //FutureTask繼承了Runnable可以作為Thread()的引數 7 Thread thread = new Thread(future); 8 thread.start(); 9 10 try { 11 //FutureTask中的get()獲取call()的回傳值 12 Integer sum = (Integer) future.get(); 13 System.out.println(sum); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } catch (ExecutionException e) { 17 e.printStackTrace(); 18 } 19 } 20 } 21 22 //實作Callable介面 23 class MyCall implements Callable { 24 25 private Integer sum = 0; 26 //執行call()方法,相當于Runnable中的run()方法不同的是call()方法可以回傳值和拋例外,不需要回傳值時可return null 27 public Object call() throws Exception { 28 for (int i = 0; i < 100; i++) { 29 if (i % 2 == 0){ 30 sum+=i; 31 } 32 } 33 return sum; 34 } 35 }
2、執行緒池創建并管理執行緒

1 public class test02 { 2 public static void main(String[] args) { 3 //Executors是執行緒池的工具類,創建10條執行緒的執行緒池 4 ExecutorService executorService = Executors.newFixedThreadPool(10); 5 6 /* 7 可以強轉成實作類設定核心池的大小、最大執行緒數、沒有執行緒任務后的存活時間 8 ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor)executorService; 9 threadPoolExecutor.setCorePoolSize(20); 10 threadPoolExecutor.setMaximumPoolSize(15); 11 threadPoolExecutor.setKeepAliveTime(1000,TimeUnit.HOURS); 12 */ 13 14 //ExecutorService介面的execute()適用于Runnable定義的執行緒方法,submit()適用于Callable定義的執行緒方法,shutdown()關閉執行緒池 15 executorService.execute(new MyRun()); 16 FutureTask future = new FutureTask(new MyCall()); 17 executorService.submit(future); 18 executorService.shutdown(); 19 20 try { 21 int sum = (Integer) future.get(); 22 System.out.println(sum); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } catch (ExecutionException e) { 26 e.printStackTrace(); 27 } 28 29 } 30 } 31 32 //實作Callable介面 33 class MyCall implements Callable { 34 35 private Integer sum = 0; 36 //執行call()方法,相當于Runnable中的run()方法不同的是call()方法可以回傳值和拋例外,不需要回傳值時可return null 37 public Object call() throws Exception { 38 for (int i = 0; i < 20; i++) { 39 if (i % 2 == 0){ 40 sum+=i; 41 } 42 } 43 return sum; 44 } 45 } 46 47 class MyRun implements Runnable { 48 49 private Integer sum = 0; 50 51 public void run() { 52 for (int i = 0; i < 20; i++) { 53 if (i % 2 != 0){ 54 System.out.println(Thread.currentThread().getName()+"["+i+"]"); 55 } 56 } 57 } 58 }
Executors.newScheduledThreadPool()
1 public class test02 { 2 public static void main(String[] args) { 3 ScheduledExecutorService scheduledThreadPool_01 = Executors.newScheduledThreadPool(5); 4 5 //schedule(Runnable command,long delay, TimeUnit unit) 延遲3秒執行 6 scheduledThreadPool_01.schedule(new Runnable() { 7 public void run() { 8 System.out.println("delay 3 seconds"); 9 } 10 }, 3, TimeUnit.SECONDS); 11 12 //scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit); 13 //延遲1秒后每3秒執行一次 14 ScheduledExecutorService scheduledThreadPool_02 = Executors.newScheduledThreadPool(5); 15 scheduledThreadPool_02.scheduleAtFixedRate(new Runnable() { 16 public void run() { 17 System.out.println("delay 1 seconds, and excute every 3 seconds"); 18 } 19 }, 1, 3, TimeUnit.SECONDS); 20 } 21 }
工具類中的這幾個創建執行緒池的方法是對ThreadPoolExecutor進行了封裝,只是各自設定的引數不同而有了各自的特點
1 //ThreadPoolExecutor(nThreads,nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); 2 // 自定義核心執行緒數量,任務佇列空間為Integer.MAX_VALUE,不使用非核心執行緒 3 Executors.newFixedThreadPool(10); 4 5 //ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>()); 6 // 只使用非核心執行緒,不使用核心執行緒和任務佇列 7 Executors.newCachedThreadPool(); 8 9 //ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()); 10 // 只使用一個核心執行緒和任務佇列 11 Executors.newSingleThreadExecutor(); 12 13 Executors.newScheduledThreadPool(10);
參考:https://www.bilibili.com/video/BV1MJ411e7SA/
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/162475.html
標籤:Java
下一篇:Spring系列.AOP使用
