1.多線程數據安全 線程同步:多個線程需要訪問同一資源時,需要以某種順序來確定該資源某一時刻只能被一個線程使用。 2.同步代碼塊實現同步(部分代碼的訪問,我們希望它同步) package com.test; public class ThreadDemo1 { public static void ...
1.多線程數據安全
線程同步:多個線程需要訪問同一資源時,需要以某種順序來確定該資源某一時刻只能被一個線程使用。從而,解決併發操作可能帶來的異常。
2.同步代碼塊實現同步(部分代碼的訪問,我們希望它同步)
synchronized(lock){ //同步代碼塊 }
其中lock就是同步監視器,它的含義是:線程開始執行同步代碼塊之前,必須先獲得對同步監視器的鎖定。任何時刻只能有一個線程可以獲得對同步監視器的鎖定,當同步代碼塊執行完成後,該線程會釋放對該同步監視器的鎖定。雖然java程式允許使用任何對象作為同步監視器,但是同步監視器的目的就是為了阻止兩個線程對同一個共用資源進行併發訪問,因此通常使用可能被併發訪問的共用資源充當同步監視器。
下麵的實例可方便理解:
package com.test; public class ThreadDemo1 { public static void main(String[] args) { // TODO Auto-generated method stub //創建共用資源對象 TicketRes ticketRes=new TicketRes(); //創建線程對象 Thread w1=new Thread(ticketRes,"視窗1"); Thread w2=new Thread(ticketRes,"視窗2"); Thread w3=new Thread(ticketRes,"視窗3"); w1.start(); w2.start(); w3.start(); } } //共用資源類 class TicketRes implements Runnable{ private int ticket=100; private Object lock=new Object();//鎖 @Override public void run() { // TODO Auto-generated method stub while(true) { synchronized (lock) { if(ticket>=1) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"賣第"+ticket+"張票"); ticket--; }else { break; } } } } }View Code
3.同步方法實現同步(強調功能,方法的訪問是同步的)
同步方法就是使用synchronized關鍵字修飾某個方法,用synchronized修飾的方法就是同步方法。這個同步方法(非static方法)無須顯式指定同步監視器,同步方法的同步監視器是this,也就是調用該方法的對象,通過同步方法可以非常方便的實現線程安全的類。
下麵的實例可方便理解:
package com.test; public class ThreadDemo2 { public static void main(String[] args) { // TODO Auto-generated method stub //創建共用資源對象 TicketRes1 ticketRes=new TicketRes1(); //創建線程對象 Thread w1=new Thread(ticketRes,"視窗1"); Thread w2=new Thread(ticketRes,"視窗2"); Thread w3=new Thread(ticketRes,"視窗3"); w1.start(); w2.start(); w3.start(); } } //共用資源類 class TicketRes1 implements Runnable{ private static int ticket=100; private Object lock=new Object();//鎖 @Override public void run() { // TODO Auto-generated method stub while(true) { if(!TicketRes1.saleTicket()) { break; } } } public synchronized static boolean saleTicket() { if(ticket>=1) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"賣第"+ticket+"張票"); ticket--; return true; }else { return false; } } }View Code
4.鎖的概念以及死鎖問題
多線程在運行的時候可能會遇到這樣的問題,多個線程要用到同一個資源,那可能會出現錯亂,比如線程要改動資源里的數據,那麼多個線程同時改就亂了套了。就像公共廁所,必須要一個一個接著上,不能兩個人或者多個人同時上。那麼鎖這個概念就是像上廁所的門,一個人在上廁所,鎖上了門,那下一個人就不能進去了。同樣的,如果我們想讓某一個程式或者某一個變數只能同時被一個線程運行,就得給程式上鎖。所以上了鎖,就能保證線程有秩序地運行。
當兩個線程相互等待對方釋放同步監視器時就會發生死鎖,Java虛擬機沒有檢測,也沒有採取措施來處理死鎖情況,所以多線程編程時應該採取措施避免死鎖出現。一旦出現死鎖,程式既不會發生任何異常,也不會給出任何提示,只是所有線程都處於阻塞狀態,無法繼續。
接下來看一個死鎖的Java實例(是不是停不下來了,就相當於迴圈語句的死迴圈)
package com.test; public class DeadLock { public static void main(String[] args) { // TODO Auto-generated method stub DeadLockThread he=new DeadLockThread(true,"小明");//他 DeadLockThread she=new DeadLockThread(false,"小華 ");//她 he.start(); she.start(); } } //線程 class DeadLockThread extends Thread{ boolean flag=false; public DeadLockThread() { } protected DeadLockThread(boolean flag,String name) { super(name); this.flag=flag; } public void run() { while(true) { if(flag) { synchronized (Lock.locka) {//他 System.out.println(Thread.currentThread().getName()+"搶到了locka"); } synchronized (Lock.lockb) { System.out.println(Thread.currentThread().getName()+"搶到了lockb"); System.out.println(Thread.currentThread().getName()+"可以上廁所了"); } }else { synchronized (Lock.lockb) {//她 System.out.println(Thread.currentThread().getName()+"搶到了lockb"); } synchronized (Lock.locka) { System.out.println(Thread.currentThread().getName()+"搶到了locka"); System.out.println(Thread.currentThread().getName()+"可以上廁所了"); } } } } } //創建鎖對象 class Lock{ public static Object locka=new Object(); public static Object lockb=new Object(); }View Code
5.線程終止
1.使用標誌,使run()方法正常執行完畢
package com.test; import java.util.Scanner; public class Demo1 { public static void main(String[] args) { // TODO Auto-generated method stub FlagStop flagStop=new FlagStop("線程1"); flagStop.start(); Scanner input=new Scanner(System.in); System.out.println("輸入任意字元結束主線程"); input.next(); flagStop.flag=false;//主線程修改其他線程的變數 System.out.println("主線程結束"); } } class FlagStop extends Thread{ //定義標誌 volatile:易揮發的、不穩定的,使用volatile修飾後,獲取變數不會從緩存中取,從記憶體中取。 volatile boolean flag=true; public FlagStop() { // TODO Auto-generated constructor stub } public FlagStop(String name) { super(name); // TODO Auto-generated constructor stub } @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"開始執行了"); while(flag) { } System.out.println(Thread.currentThread().getName()+"執行完畢"); } }View Code
2.使用stop方法強制終止線程
package com.test; import java.util.Scanner; public class Demo2 { public static void main(String[] args) { // TODO Auto-generated method stub WordStop wordStop=new WordStop("線程1"); wordStop.start(); Scanner input=new Scanner(System.in); System.out.println("輸入任意字元結束主線程"); input.next(); wordStop.stop(); wordStop.flag=false;//主線程修改其他線程的變數 System.out.println("主線程結束"); } } class WordStop extends Thread{ //定義標誌 volatile:易揮發的、不穩定的,使用volatile修飾後,獲取變數不會從緩存中取,從記憶體中取。 volatile boolean flag=true; public WordStop() { // TODO Auto-generated constructor stub } public WordStop(String name) { super(name); // TODO Auto-generated constructor stub } @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"開始執行了"); while(flag) { } System.out.println(Thread.currentThread().getName()+"執行完畢"); } }View Code
3.使用interrupt方法中斷線程
package com.test; import java.io.IOException; public class Demo3 { public static void main(String[] args) throws IOException, InterruptedException { // TODO Auto-generated method stub InterruptStop interruptStop=new InterruptStop(); interruptStop.start(); System.out.println("在10秒之內輸入任意符號結束"); System.in.read(); interruptStop.interrupt();//打斷正在休眠的線程 interruptStop.join(); System.out.println("主線程結束..."); } } class InterruptStop extends Thread{ @Override public void run() { try { Thread.sleep(10000);//拋出一個異常InterruptedException } catch (InterruptedException e) { // TODO Auto-generated catch block // e.printStackTrace(); System.out.println("執行了catch"); } System.out.println("子線程執行完畢"); } }View Code
6.線程間通信
1.wait:掛起當前線程,釋放共用資源鎖
2.notify:在所有的wait線程當中隨機選擇一條喚醒
3.notifyAll:喚醒全部wait線程