一,問題背景
1.為什么要引入多線程?
用多線程只有一個目的,那就是更好的利用cpu的資源,因為所有的多線程代碼都可以用單線程來實現。說這個話其實只有一半對,因為反應“多角色”的程序代碼,最起碼每個角色要給他一個線程吧,否則連實際場景都無法模擬,當然也沒法說能用單線程來實現:比如最常見的“生產者,消費者模型”。
2.多線程、同步、並發概念:
多線程:指的是這個程序(一個進程)運行時產生了不止一個線程。
並行:多個cpu實例或者多台機器同時執行一段處理邏輯,是真正的同時。
並發:通過cpu調度算法,讓用戶看上去同時執行,實際上從cpu操作層面不是真正的同時。並發往往在場景中有公用的資源,那么針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。
3.引入多線程后會帶來那些問題?
java允許多線程並發控制,當多個線程同時操作一個可共享的資源變量時(如數據的增刪改查),將會導致數據不准確,相互之間產生沖突,因此加入同步鎖以避免在該線程沒有完成操作之前,被其他線程的調用,從而保證了該變量的唯一性和准確性。
以買票系統為例,我們發現不加控制多線程會出現超賣現象。
1 public class RunnableImpl implements Runnable { 2 3 private int ticket=100; 4 @Override 5 public void run() { 6 while(true){ 7 if (ticket<=0){ 8 break; 9 } 10 try { 11 Thread.sleep(200); 12 String name = Thread.currentThread().getName(); 13 System.out.println("第"+name+"窗口正在賣出第"+(200-ticket+1)+"張票,剩余"+(--ticket)+"張"); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 18 } 19 20 21 22 } 23 }
測試類:
1 public class originticket { 2 public static void main(String[] args) { 3 RunnableImpl runnable = new RunnableImpl(); 4 new Thread(runnable,"一").start(); 5 new Thread(runnable,"二").start(); 6 } 7 }
結果顯示:
二,問題解決方案之synchronize代碼塊:
1 public class RunnableImplsyn implements Runnable { 2 3 private int ticket=200; 4 @Override 5 public void run() { 6 while(true) { 7 try { 8 Thread.sleep(200); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 synchronized (this) { 13 if (ticket <= 0) { 14 break; 15 } 16 17 18 String name = Thread.currentThread().getName(); 19 System.out.println("第" + name + "窗口正在賣出第" + (200 - ticket + 1) + "張票,剩余" + (--ticket) + "張"); 20 21 22 } 23 } 24 25 26 } 27 }
測試類:
1 public class synchronizeblockTicket { 2 3 public static void main(String[] args) { 4 RunnableImplsyn runnableImplsyn = new RunnableImplsyn(); 5 new Thread(runnableImplsyn,"一").start(); 6 new Thread(runnableImplsyn,"二").start(); 7 } 8 }
結果:
三,問題解決方案之synchronize方法:
這里抽取了方法,this代指:
1 public class RunnableImplsynfn implements Runnable { 2 3 private static int ticket=200; 4 @Override 5 public void run() { 6 test(); 7 8 } 9 10 11 private synchronized void test() { 12 while(true){ 13 if (ticket<=0){ 14 break; 15 } 16 try { 17 Thread.sleep(200); 18 String name = Thread.currentThread().getName(); 19 System.out.println("第"+name+"窗口正在賣出第"+(200-ticket+1)+"張票,剩余"+(--ticket)+"張"); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 24 } 25 26 27 28 } 29 }
測試類:
1 public class synchronizefnTicket { 2 3 4 public static void main(String[] args) { 5 RunnableImplsynfn runnableImplsyn = new RunnableImplsynfn(); 6 new Thread(runnableImplsyn,"一").start(); 7 new Thread(runnableImplsyn,"二").start(); 8 } 9 }
四,問題解決方案之靜態synchronize方法:
同上
1 public class RunnableImplsynfn implements Runnable { 2 3 private static int ticket=200; 4 @Override 5 public void run() { 6 statictest(); 7 8 } 9 10 private static synchronized void statictest() { 11 while(true){ 12 if (ticket<=0){ 13 break; 14 } 15 try { 16 Thread.sleep(200); 17 String name = Thread.currentThread().getName(); 18 System.out.println("第"+name+"窗口正在賣出第"+(200-ticket+1)+"張票,剩余"+(--ticket)+"張"); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 23 } 24 25 } 26 27 28 } 29 }
測試類一樣略。
五,問題解決方案之lock方法:
1 import java.util.concurrent.locks.Lock; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 public class Runnablelock implements Runnable { 5 private int ticket=200; 6 Lock l= new ReentrantLock(); 7 8 @Override 9 public void run() { 10 while(true){ 11 /* try { 12 Thread.sleep(20); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 }*/ 16 l.lock();//從此以后加鎖 17 18 if (ticket<=0){ 19 break; 20 } 21 try { 22 Thread.sleep(200); 23 24 String name = Thread.currentThread().getName(); 25 System.out.println("第"+name+"窗口正在賣出第"+(200-ticket+1)+"張票,剩余"+(--ticket)+"張"); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 }finally { 29 l.unlock();//釋放鎖 30 } 31 32 } 33 34 35 } 36 }
測試類:
1 public class lockTicket { 2 3 4 public static void main(String[] args) { 5 Runnablelock runnablelock = new Runnablelock(); 6 new Thread(runnablelock,"一").start(); 7 new Thread(runnablelock,"二").start(); 8 } 9 }