Java笔记-多线程中同步加锁相关
Java程序入口就是由JVM啟動的main線程:
main線程又可以啟動其他線程。當所有線程都運行結束時JVM退出,進程結束。
?
守護線程(Daemon):守護線程是為其他線程服務的線程,所有的非守護線程都執行完畢后,虛擬機才會退出。
守護線程的特點:不能持有資源(如打開文件等)
?
創建守護線程:
setDaemon(true);下面來演示下,子線程中有死循環,而主線程退出了,子線程還沒退出。
程序運行截圖如下:
主線程已經退出,而子線程沒有退出。
?
源碼如下:
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");} }加上守護線程后,即可在main函數退出后,頁退出!
程序運行截圖如下:
源碼如下:
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");} }下面是線程同步相關的設置:
多個線程同時運行,線程調度由操作系統決定,程序本身無法決定。
如下多個線程同時讀取共享數據時:
會出現問題,程序運行截圖如下:
代碼如下:
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");} }對共享數據進行寫入時,必須要保證是原子操作
Java使用synchronized對一個對象進行加鎖!
synchronized的缺陷:性能會下降;
synchronized的使用:
找出修改共享變量線程代碼塊;選擇一個實例作為鎖;使用synchronized(lockObject){...}
運行截圖如下:
代碼如下:
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:不用擔心異常!
public void add(int m){synchronized (obj){if(m < 0){throw new RuntimeException();}this.value += m;}//無論有無異常,都會在此釋放 }JVM規范定義了幾種原子操作:
基本類型(long和double除外)賦值:int n = 100;
引用類型的賦值:List<String> list = anotherList;
?
下面是關于synchronized其他的寫法
public synchronized void add(int n){count += n;total += n; }public void add(int n){synchronized(this){count += n;total += n;} }上面這兩個寫法是等價的。
?
如果一個類被設計為允許多線程正確訪問:這個類就是“線程安全”的(thread-safe)
如java.lang.StringBuffer就是線程安全的。
?
synchronized是可以重復使用的。如下:
public void add(int m){synchronized (lockA){this.value += m;synchronized (lockB){this.another += m;}//釋放lockB}//釋放lockA }這里要注意:不同線程獲取多個不同對象的鎖可能會導致死鎖。
死鎖形成的條件(注意了,不管是哪個編程語言都差不多):
兩個線程各自持有不同的鎖;
兩個線程各自試圖獲取對方已持有的鎖;
雙方無限等待下去:導致死鎖。
死鎖發送后:沒有任何機制能解除死鎖;只能強制結束JVM進程。
如何避免死鎖:線程獲取鎖的順序要一致!
如下實例!
源碼如下:
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");} }這個代碼是有死鎖的但不一定發送:
原因是這樣的:
此處只要把鎖的先后改成一樣的就可以了!
總結
以上是生活随笔為你收集整理的Java笔记-多线程中同步加锁相关的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++设计模式-单例模式(双重锁定)
- 下一篇: HTTP、HTTPS、SSL、TLS之间