==多線程的建立方法有:四種!!! #F44336==java
先上代碼:面試
/** * 建立多線程的方法一: * 建立繼承Thread的子類 * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/7 18:41 */ //線程類 class NumCount extends Thread{ //run方法裏是要執行的代碼 @Override public void run() { //輸出0-99 for (int i = 0; i < 100; i++) { System.out.println(NumCount.currentThread().getName()+":"+i); } } } //主類 public class MyThread { public static void main(String[] args) { //建立Thread子類的對象 NumCount nc1 = new NumCount(); //給線程起個名字 nc1.setName("計數線程1"); //開啓線程 nc1.start(); } }
注意事項windows
先上代碼:安全
/** * 建立多線程的方法二: * 實現Runnable接口 * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/7 18:41 */ //線程類 class NumCount implements Runnable{ //run方法裏是要執行的代碼 @Override public void run() { //輸出0-99 for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } //主類 public class MyThread { public static void main(String[] args) { //實例化Runnable接口 NumCount numberCount = new NumCount(); //實例化一個線程,構造方法的參數爲實例化的接口 Thread nc1 = new Thread(numberCount); //給線程起個名字 nc1.setName("計數線程1"); //開啓線程 nc1.start(); } }
注意事項多線程
先上代碼:ide
/** * 建立多線程的方法三: * 實現Callable接口 --- JDK5.0新增 * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/7 18:41 */ //線程類 class NumCount implements Callable<Integer> { //重寫call()方法 @Override public Integer call() throws Exception { //計算1-100的數的和 int sum = 0; for (int i = 1; i <= 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); sum += i; } return sum; } } //主類 public class MyThread { public static void main(String[] args) throws ExecutionException, InterruptedException { //建立Callable接口實現類的對象 NumCount numCount = new NumCount(); //將Callable接口實現類的對象傳遞到FutureTask構造器,建立FutureTask對象 FutureTask<Integer> futureTask = new FutureTask<>(numCount); //建立線程,傳遞FutureTask對象到Thread構造器中 Thread nc1 = new Thread(futureTask); nc1.setName("計數線程1"); nc1.start(); //獲取Callable中call方法的返回值 Integer sum = futureTask.get(); System.out.println("總和爲:" + sum); } }
注意事項this
先上代碼:線程
/** * 建立多線程的方法四: * 建立線程池 * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/7 18:41 */ //線程類 class NumCount implements Runnable { //重寫run方法 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } //主類 public class MyThread { public static void main(String[] args) throws ExecutionException, InterruptedException { //建立指定線程數量的線程池 ExecutorService service = Executors.newFixedThreadPool(10); //執行指定的線程的操做,須要提供實現Runnable接口或Callable接口實現類的對象 service.execute(new NumCount());//用於Runnable // service.submit();//用於Callable //關閉線程池 service.shutdown(); } }
注意事項設計
Q: 爲何會重票錯票?
A: 當出現極限狀況:當多個線程同時調用共享數據,而且線程還未結束,就會出現重複調取同一個值的狀況(重票),票數不知足線程數時,會出現負數(錯票)。code
#### 解決方案:線程同步機制
線程同步機制:
當某一個線程在使用共用的數據(執行被同步的代碼)時,其餘線程要進行等候,不管這個線程是否處於阻塞狀態。
若是不明白的話,就想象一下旅遊景點女廁所門口排隊的女性朋友們。
#### 具體辦法:
1. 同步代碼塊
這個方法涉及到synchronized修飾詞。
基本格式:
synchronized(同步監視器(鎖)){ * //要被同步的代碼 * }
說明:
使用示例:
/** * 用同步代碼塊解決線程安全問題 * * 問題:三個窗口賣100張票,用線程解決。 * * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/5 17:50 */ class TicketTread extends Thread{ //保證多個線程共用一份數據,須要設置成靜態的 public static int ticket = 100; //同一把鎖 public static Object object = new Object(); @Override public void run() { while (true){ //同步代碼塊 synchronized (object){ if (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(TicketTread.currentThread().getName() + ":正在售賣第" + ticket + "張票"); ticket--; }else{ break; } } } } } public class TicketDemo { public static void main(String[] args) { TicketTread t1 = new TicketTread(); TicketTread t2 = new TicketTread(); TicketTread t3 = new TicketTread(); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
2. 同步方法
這個方法涉及到synchronized修飾詞。
基本格式:
synchronized 數據類型 方法名(){ //須要被同步的代碼 }
注意:
使用示例:
class Windows1 implements Runnable{ //這裏無需設定靜態變量,由於多個線程調用同一個接口 public int ticket = 100; Object object = new Object(); @Override public void run() { while (true){ show(); } } //同步方法 public synchronized void show(){ if (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(TicketTread.currentThread().getName() + ":正在售賣第" + ticket + "張票"); ticket--; } } } public class TicketDemo2 { public static void main(String[] args) { Windows1 windows = new Windows1(); Thread t1 = new Thread(windows); Thread t2 = new Thread(windows); Thread t3 = new Thread(windows); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
3. ReentrantLock
jdk1.5新特性
用法和同步代碼塊相似。用調用方法的辦法替代代碼塊。
使用示例:
class Windows implements Runnable{ public static int ticket = 100; private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true){ try { //調用鎖定的方法 lock.lock(); //lock以後的代碼至關於同步代碼塊的效果 if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":正在售賣第" + ticket + "張票"); ticket--; } else { break; } } finally { //調用解鎖的方法 lock.unlock(); } } } } public class LockTest { public static void main(String[] args) { Windows windows = new Windows(); Thread t1 = new Thread(windows); Thread t2 = new Thread(windows); Thread t3 = new Thread(windows); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
面試題:synchronized 和 Lock 的區別?
兩個線程打印1-100,交替打印
問題代碼:
/** * 兩個線程打印1-100,交替打印 * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/6 22:41 */ class Number implements Runnable{ private int number = 1; @Override public void run() { while (true){ synchronized (this) { if (number<=100){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":數字:" + number); number++; }else { break; } } } } } public class Communicate { public static void main(String[] args) { Number number = new Number(); Thread t1 = new Thread(number); Thread t2 = new Thread(number); t1.setName("線程1"); t2.setName("線程2"); t1.start(); t2.start(); } }
當你運行此段代碼時,會出現僅單一線程運行的狀況
t1線程一致佔用鎖,沒法進行交替打印
須要利用三劍客:wait(),notify(),notifyAll()
說明:
/** * 兩個線程打印1-100,交替打印 * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/6 22:41 */ class Number implements Runnable{ private int number = 1; @Override public void run() { while (true){ synchronized (this) { //使用notify()方法喚醒線程 // this.notify(); notify(); if (number<=100){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":數字:" + number); number++; try { //使用wait()方法使得線程處於阻塞狀態 // this.wait(); wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { break; } } } } } public class Communicate { public static void main(String[] args) { Number number = new Number(); Thread t1 = new Thread(number); Thread t2 = new Thread(number); t1.setName("線程1"); t2.setName("線程2"); t1.start(); t2.start(); } }
面試題:sleep() 和 wait() 的異同?
問題解決:
/** * 用線程同步的方法解決單例模式的線程安全問題 * * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/6 16:32 */ public class BankDemo { } class Bank{ //單例模式(private構造方法,確保該類使用對象惟一) private Bank(){}; public static Bank instence = null; //此處可能引起線程安全問題 public static Bank getInstence(){ //方式一,效率稍差(線程所有同步) // synchronized (Bank.class) { // if (instence==null){ // instence = new Bank(); // } // return instence; // } //方式二:效率較高(一小部分線程同步) if (instence==null){ synchronized (Bank.class) { if (instence==null){ instence = new Bank(); } } } return instence; } }
問題描述:
銀行有一個帳戶,有兩個儲戶分別向同一個帳戶存3000元,每次存1000,存3次,每次存完打印帳餘額。
問題解決:
/** * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/6 22:19 */ //帳戶(共享數據) class Account{ private double balance; public Account(double balance){ this.balance =balance; } //存錢 public synchronized void deposit(double amt){ if (amt > 0){ balance += amt; System.out.println(Thread.currentThread().getName() + ":存錢成功!當前餘額爲:" + balance); } } } //儲戶(線程) class Customer implements Runnable{ private Account acct; //初始化數據 public Customer(Account acct){ this.acct = acct; } @Override public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } acct.deposit(1000); } } } public class AccountTest { public static void main(String[] args) { Account acct = new Account(0); Customer customer = new Customer(acct); Thread c1 = new Thread(customer); Thread c2 = new Thread(customer); c1.setName("甲"); c2.setName("乙"); c1.start(); c2.start(); } }
問題描述:
生產者(Productor)將產品交給店員(Clerk),而消費者(Customer)從店員處取走產品。店員一次只能持有固定數量的產品(好比:20),若是生產者試圖生產更多的產品,店員會叫生產者停一下,若是店中有空位放產品了再通知生產者繼續生產;若是店中沒有產品了,店員會告訴消費者等一下,若是店中有產品了再通知消費者來取走產品。
問題分析:
1.是不是多線程問題?是,生產者線程,消費者線程
2.是否有共享數據?是,店員(或產品)
3.如何解決現成的安全問題?同步機制,有三種方法
4.是否設計線程的通訊?是
問題解決:
/** * @author 🏹☂࿈秋鶩࿈🏹️ * @create 2020/3/7 11:04 */ class Clerk{ private int productCount = 0; //生產產品 public synchronized void produceProduct() { if (productCount<20){ productCount++; System.out.println(Thread.currentThread().getName() + ":正在生產第" + productCount + "個產品"); notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } //消費產品 public synchronized void consumeProduct(){ if (productCount>0){ System.out.println(Thread.currentThread().getName() + ":正在消費第" + productCount + "個產品"); productCount--; notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer implements Runnable{//生產者 private Clerk clerk; public Producer(Clerk clerk){ this.clerk = clerk; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ":開始生產產品...."); while (true){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } clerk.produceProduct(); } } } class Consumer implements Runnable{//消費者 private Clerk clerk; public Consumer(Clerk clerk){ this.clerk = clerk; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ":開始消費產品...."); while (true){ try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } clerk.consumeProduct(); } } } public class Product { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer producer = new Producer(clerk); Thread p1 = new Thread(producer); p1.setName("生產者1"); Consumer consumer = new Consumer(clerk); Thread c1 = new Thread(consumer); c1.setName("消費者1"); Thread c2 = new Thread(consumer); c2.setName("消費者2"); p1.start(); c1.start(); c2.start(); } }