JAVA基礎-多線程
一、線程與進程
進程:正在計算機中運行的一個程序,當一個程序進入內存運行,即變成一個進程。一個軟件至少有一個進程,
有的軟件在點擊一個應用圖標是可能會給你開多個進程,如:360全家桶。
線程:軟件在計算機中執行的一條路徑。軟件可以是多線程的。
總結:一個程序運行後至少有一個進程,一個進程中可以包含多個線程。而多線程就是一個程序中有多個線程
在同時執行。而JAVA為我們提供了2種方式實現多線程:1、繼承Thread類 2、實現Runnable接口。我們在編寫程
序時的主方法就是一個單獨的線程,線程名叫 "main"。而JAVA虛擬機Jvm運行是多線程運行。
二、Thread類
JAVA實現多線程其中的一種方法就是繼承Thread類,Thread是程序中的執行線程。Java 虛擬機允許應用程序並
發地運行多個執行線程。Thread類常用的構造方法有:1、分配新的 Thread 對象 - Thread(),2、分配新的Thread
對象,將指定的name作為線程名稱 - Thread(String name)。其常用的方法有:
1、改變線程名稱,使之與參數 name 相同:setName(String name)
2、該線程要執行的操作:run()
3、在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行):sleep()
4、使該線程開始執行;Java 虛擬機調用該線程的 run 方法:start()
5、返回該線程的名稱:getName()
6、靜態方法,返回值為Thread對象 - 返回對當前正在執行的線程對象的引用:currentThread()
A:創建線程的步驟:
1.定義一個類繼承Thread。
2.重寫run方法。
3.創建子類對象(創建線程對象)。
4.調用start方法,開啟線程並讓線程執行,同時還會告訴jvm去調用run方法。
舉例:
1 //定義類MyThread繼承Thread類 2 public class MyThread extends Thread { 3 @Override 4 // 重寫Thread類裏的run方法 5 public void run() { 6 // 想要多線程執行的代碼 7 for (int i = 0; i < 100; i++) { 8 // 獲取線程名稱和循環的次數 9 System.out.println(getName() + " " + i); 10 } 11 } 12 }
主函數:
1 public static void main(String[] args) {2 // 創建線程一 3 MyThread myThread01 = new MyThread(); 4 // 為線程一設置線程名 5 myThread01.setName("線程一"); 6 // 開啟線程一 7 myThread01.start(); 8 // 創建線程二 9 MyThread myThread02 = new MyThread(); 10 // 為線程二設置線程名 11 myThread02.setName("線程二"); 12 // 開啟線程二 13 myThread02.start(); 14 }
三、Runnable接口
JAVA實現多線程的另一種方法就是實現Runnable接口,實現 run 方法。然後創建Runnable的子類對象,
傳入到某個線程的構造方法中,開啟線程。Runnable接口其實就是用來指定每個線程要執行的任務,在由線
程類Thread來執行這些任務。Runnable接口只有一個要實現的方法:run(),作用與Thread類的run方法類似,
在啟動該線程時將被調用,也就是說將由其實現類來實現run方法。Runnable接口的常用實現類是Thread類。
B:創建線程的步驟:
1、定義類實現Runnable接口。
2、覆蓋接口中的run方法。
3、創建Thread類的對象
4、將Runnable接口的子類對象作為參數傳遞給Thread類的構造函數。
5、調用Thread類的start方法開啟線程。
舉例:
1 //創建MyRunable類實現Runnable接口 2 public class MyRunable implements Runnable { 3 @Override 4 //重寫run方法 5 public void run() { 6 /*循環 7 Thread.currentThread().getName: 8 通過Thread類的靜態方法currentThread()方法,返回正在執行的線程對象,並獲取線程名*/ 9 for (int i = 0; i < 100; i++) { 10 System.out.println(Thread.currentThread().getName() + " :" + i); 11 } 12 } 13 }
*主方法:
1 public static void main(String[] args) { 2 // 獲取MyRunable對象(線程任務) 3 MyRunable r = new MyRunable(); 4 // 根據線程任務創建線程一 5 Thread t1 = new Thread(r); 6 // 命名 7 t1.setName("線程一"); 8 // 開啟線程 9 t1.start(); 10 // 根據線程任務創建線程二 11 Thread t2 = new Thread(r); 12 t2.setName("線程二"); 13 t2.start(); 14 }
四、多線程安全
由於CUP對線程的處理具有隨機性,這就導致了當線程1進入某一循環,還沒有執行循環裏的語句時,
CUP突然切換到線程2,線程2也進入了循環語句。這樣就會導致本應該只執行一次的條件執行了2次,導致
線程不安全的問題出現。
問題出現的原因:
* 要有多個線程
* 要有被多個線程所共享的數據
* 多個線程並發的訪問共享的數據。
用實現Runnable接口的方式對同一數據進行
操作可能就會出現這種重復的問題:
(一)、多線程安全的解決
問題出現了該如何解決呢?多線程處理的機制其實和我們在火車上廁所類似。在火車上有好多人都要去廁所,
但是廁所只能供一個人使用,2個人如果一起擠進了廁所就會發生一些“事情”,那麽火車是怎麽解決這個問題的呢?
我們可以在廁所的門上上一把鎖,路人甲進廁所後把門鎖起來,方便完之後再把鎖打開,路人乙在進去把門鎖上。
而JAVA為了解決多線程的並發安全問題,也給我們提供了這麽一把“鎖” - 同步代碼塊和同步方法。
1、同步代碼塊:
synchronized(鎖對象){
}
舉例:
1 public class MyRunable implements Runnable { 2 // 設置一個被操作的數據 3 int i = 100; 4 5 @Override 6 public void run() { 7 while (true) { 8 // 設置同步代碼塊 9 synchronized (MyRunable.class) { 10 try { 11 // 為了不要太快,先睡一會 12 Thread.sleep(200); 13 } catch (InterruptedException e) { 14 // TODO Auto-generated catch block 15 e.printStackTrace(); 16 } 17 // 對數據進行操作 18 if (i > 0) { 19 System.out.println(Thread.currentThread().getName() + " " + i--); 20 } 21 } 22 } 23 } 24 }
*入口方法:
1 public static void main(String[] args) { 2 MyRunable runable = new MyRunable(); 3 Thread t1 = new Thread(runable); 4 t1.setName("線程A"); 5 Thread t2 = new Thread(runable); 6 t2.setName("線程B"); 7 Thread t3 = new Thread(runable); 8 t3.setName("線程C"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 }
鎖對象:可以是java中的任意對象。多個同步代碼塊必須使用同一個對象,才能實現多個代碼塊之間的同步。一
般使用當前類的.class對象
2、同步方法:
非靜態方法:this
靜態方法:字節碼對象(類名.class)
舉例:
1 public class MyRunable implements Runnable { 2 // 設置一個被操作的數據 3 int i = 100; 4 5 public void run() { 6 while (true) { 7 method(); 8 } 9 } 10 11 private synchronized void method() { 12 13 try { 14 Thread.sleep(150); 15 } catch (InterruptedException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } 19 if (i > 0) { 20 System.out.println(Thread.currentThread().getName() + " :" + i); 21 i--; 22 }else { 23 System.exit(0); 24 } 25 }
*入口方法:
1 public static void main(String[] args) { 2 MyRunable runable = new MyRunable(); 3 Thread t1 = new Thread(runable); 4 t1.setName("線程A"); 5 Thread t2 = new Thread(runable); 6 t2.setName("線程B"); 7 Thread t3 = new Thread(runable); 8 t3.setName("線程C"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 }
3、優缺點
* 同步:安全性高,效率低。
* 非同步:效率高,但是安全性低。
五、使用多線程對同一數據進行不同的操作
代碼:
1 public class Demo { 2 public static class AddSub { 3 //提供原子操作的Integer的類(無鎖的線程安全整數 AtomicInteger) 4 AtomicInteger at = new AtomicInteger(1); 5 int j = 1; 6 7 public static void main(String[] args) { 8 AddSub ab = new AddSub(); 9 Add a = ab.new Add(); 10 Sub b = ab.new Sub(); 11 Thread t1 = new Thread(a); 12 Thread t2 = new Thread(a); 13 Thread t3 = new Thread(b); 14 Thread t4 = new Thread(b); 15 t1.start(); 16 t2.start(); 17 t3.start(); 18 t4.start(); 19 } 20 21 public synchronized void add() { 22 j++; 23 System.out.println("add:" + j); 24 } 25 26 public synchronized void sub() { 27 j--; 28 System.out.println("sub:" + j); 29 } 30 31 class Add implements Runnable { 32 @Override 33 public void run() { 34 for (int i = 0; i < 20; i++) { 35 add(); 36 } 37 38 } 39 } 40 41 class Sub implements Runnable { 42 @Override 43 public void run() { 44 for (int i = 0; i < 20; i++) { 45 sub(); 46 } 47 48 } 49 } 50 } 51 }
JAVA基礎-多線程