Java笔记-多线程中同步加锁相关
Java程序入口就是由JVM啟動(dòng)的main線程:
main線程又可以啟動(dòng)其他線程。當(dāng)所有線程都運(yùn)行結(jié)束時(shí)JVM退出,進(jìn)程結(jié)束。
?
守護(hù)線程(Daemon):守護(hù)線程是為其他線程服務(wù)的線程,所有的非守護(hù)線程都執(zhí)行完畢后,虛擬機(jī)才會退出。
守護(hù)線程的特點(diǎn):不能持有資源(如打開文件等)
?
創(chuàng)建守護(hù)線程:
setDaemon(true);下面來演示下,子線程中有死循環(huán),而主線程退出了,子線程還沒退出。
程序運(yùn)行截圖如下:
主線程已經(jīng)退出,而子線程沒有退出。
?
源碼如下:
import java.text.SimpleDateFormat; import java.util.Date;class MyThread1 extends Thread{@Overridepublic void run() {while (true) {Date date = new Date(System.currentTimeMillis());SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");System.out.println(formatter.format(date));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }public class Main1 {public static void main(String[] args) throws InterruptedException {System.out.println("BEGIN");MyThread1 myThread1 = new MyThread1();myThread1.start();Thread.sleep(1000 * 3);System.out.println("OVER");} }加上守護(hù)線程后,即可在main函數(shù)退出后,頁退出!
程序運(yùn)行截圖如下:
源碼如下:
import java.text.SimpleDateFormat; import java.util.Date;class MyThread1 extends Thread{@Overridepublic void run() {while (true) {Date date = new Date(System.currentTimeMillis());SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");System.out.println(formatter.format(date));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }public class Main1 {public static void main(String[] args) throws InterruptedException {System.out.println("BEGIN");MyThread1 myThread1 = new MyThread1();myThread1.setDaemon(true);myThread1.start();Thread.sleep(1000 * 3);System.out.println("OVER");} }下面是線程同步相關(guān)的設(shè)置:
多個(gè)線程同時(shí)運(yùn)行,線程調(diào)度由操作系統(tǒng)決定,程序本身無法決定。
如下多個(gè)線程同時(shí)讀取共享數(shù)據(jù)時(shí):
會出現(xiàn)問題,程序運(yùn)行截圖如下:
代碼如下:
class AddThread extends Thread{@Overridepublic void run() {for(int i = 0; i < Main2.LOOP; i++){Main2.count += 1;}} }class DecThread extends Thread{@Overridepublic void run() {for(int i = 0; i < Main2.LOOP; i++){Main2.count -= 1;}} }public class Main2 {final static int LOOP = 10000;public static int count = 0;public static void main(String[] arg) throws InterruptedException {AddThread addThread = new AddThread();DecThread decThread = new DecThread();addThread.start();decThread.start();addThread.join();decThread.join();System.out.println(count);System.out.println("OVER");} }對共享數(shù)據(jù)進(jìn)行寫入時(shí),必須要保證是原子操作
Java使用synchronized對一個(gè)對象進(jìn)行加鎖!
synchronized的缺陷:性能會下降;
synchronized的使用:
找出修改共享變量線程代碼塊;選擇一個(gè)實(shí)例作為鎖;使用synchronized(lockObject){...}
運(yùn)行截圖如下:
代碼如下:
class AddThread3 extends Thread{@Overridepublic void run() {for(int i = 0; i < Main2.LOOP; i++){synchronized (Main3.LOCK) {Main2.count += 1;}}} }class DecThread3 extends Thread{@Overridepublic void run() {for(int i = 0; i < Main2.LOOP; i++){synchronized (Main3.LOCK) {Main2.count -= 1;}}} }public class Main3 {final static int LOOP = 10000;public static int count = 0;public static final Object LOCK = new Object();public static void main(String[] arg) throws InterruptedException {AddThread3 addThread = new AddThread3();DecThread3 decThread = new DecThread3();addThread.start();decThread.start();addThread.join();decThread.join();System.out.println(count);System.out.println("OVER");} }使用synchronized:不用擔(dān)心異常!
public void add(int m){synchronized (obj){if(m < 0){throw new RuntimeException();}this.value += m;}//無論有無異常,都會在此釋放 }JVM規(guī)范定義了幾種原子操作:
基本類型(long和double除外)賦值:int n = 100;
引用類型的賦值:List<String> list = anotherList;
?
下面是關(guān)于synchronized其他的寫法
public synchronized void add(int n){count += n;total += n; }public void add(int n){synchronized(this){count += n;total += n;} }上面這兩個(gè)寫法是等價(jià)的。
?
如果一個(gè)類被設(shè)計(jì)為允許多線程正確訪問:這個(gè)類就是“線程安全”的(thread-safe)
如java.lang.StringBuffer就是線程安全的。
?
synchronized是可以重復(fù)使用的。如下:
public void add(int m){synchronized (lockA){this.value += m;synchronized (lockB){this.another += m;}//釋放lockB}//釋放lockA }這里要注意:不同線程獲取多個(gè)不同對象的鎖可能會導(dǎo)致死鎖。
死鎖形成的條件(注意了,不管是哪個(gè)編程語言都差不多):
兩個(gè)線程各自持有不同的鎖;
兩個(gè)線程各自試圖獲取對方已持有的鎖;
雙方無限等待下去:導(dǎo)致死鎖。
死鎖發(fā)送后:沒有任何機(jī)制能解除死鎖;只能強(qiáng)制結(jié)束JVM進(jìn)程。
如何避免死鎖:線程獲取鎖的順序要一致!
如下實(shí)例!
源碼如下:
class SharedObject{final Object lockA = new Object();final Object lockB = new Object();int accountA = 1000;int accountB = 2000;public void a2b(int balance){synchronized (lockA){accountA -= balance;synchronized (lockB){accountB += balance;}}}public void b2a(int balance){synchronized (lockB){accountB -= balance;synchronized (lockA){accountA += balance;}}} }class AThread extends Thread{@Overridepublic void run() {for(int i = 0; i < Main4.LOOP; i++){Main4.shared.a2b(1);System.out.println("ing...");}} }class BThread extends Thread{@Overridepublic void run() {Main4.shared.b2a(1);for(int i = 0; i < Main4.LOOP; i++){System.out.println("ing...");}} }public class Main4 {final static int LOOP = 100000;static SharedObject shared = new SharedObject();public static void main(String[] args) throws InterruptedException {Thread t1 = new AThread();Thread t2 = new AThread();t1.start();t2.start();t1.join();t2.join();System.out.println("OVER");} }這個(gè)代碼是有死鎖的但不一定發(fā)送:
原因是這樣的:
此處只要把鎖的先后改成一樣的就可以了!
總結(jié)
以上是生活随笔為你收集整理的Java笔记-多线程中同步加锁相关的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++设计模式-单例模式(双重锁定)
- 下一篇: java美元兑换,(Java实现) 美元