【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例
生活随笔
收集整理的這篇文章主要介紹了
【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Thread 的生命周期
一、實現Runnable接口方式
1、在 run 方法中使用 synchronized 塊
/*** 例子:創建三個窗口賣票,總票數為100張.使用實現Runnable接口的方式* 1.問題:賣票過程中,出現了重票、錯票 -->出現了線程的安全問題* 2.問題出現的原因:當某個線程操作車票的過程中,尚未操作完成時,其他線程參與進來,也操作車票。* 3.如何解決:當一個線程a在操作ticket的時候,其他線程不能參與進來。直到線程a操作完ticket時,* 其他線程才可以開始操作ticket。這種情況即使線程a出現了阻塞,也不能被改變。* 4.在Java中,我們通過同步機制,來解決線程的安全問題。* 方式一:同步代碼塊* synchronized(同步監視器){* //需要被同步的代碼* }* 說明:* 1.操作共享數據的代碼,即為需要被同步的代碼。 -->不能包含代碼多了,也不能包含代碼少了。* 2.共享數據:多個線程共同操作的變量。比如:ticket就是共享數據。* 3.同步監視器,俗稱:鎖。任何一個類的對象,都可以充當鎖。* 要求:多個線程必須要共用同一把鎖。* 補充:在實現Runnable接口創建多線程的方式中,我們可以考慮使用this充當同步監視器。* 方式二:同步方法。* 如果操作共享數據的代碼完整的聲明在一個方法中,我們不妨將此方法聲明同步的。* 5.同步的方式,解決了線程的安全問題。---好處* 操作同步代碼時,只能有一個線程參與,其他線程等待。相當于是一個單線程的過程,效率低。 ---局限性*/ class TicketWindow implements Runnable {private int ticket = 100;// Object obj = new Object();//正確@Overridepublic void run() { // Object obj = new Object();//錯誤while (true) { // synchronized (obj){synchronized (this) {//此時的this:唯一的Window1的對象if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket);ticket--;} else {break;}}}} }public class Main1 {public static void main(String[] args) {TicketWindow ticketWindow = new TicketWindow();Thread t1 = new Thread(ticketWindow);Thread t2 = new Thread(ticketWindow);Thread t3 = new Thread(ticketWindow);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }2、使用syncronized方法
/*** 使用同步方法解決實現Runnable接口的線程安全問題* 關于同步方法的總結:* 1. 同步方法仍然涉及到同步監視器,只是不需要我們顯式的聲明。* 2. 非靜態的同步方法,同步監視器是:this* 靜態的同步方法,同步監視器是:當前類本身*/ class TicketWindow3 implements Runnable {private int ticket = 100;@Overridepublic void run() {while (true) {show();}}private synchronized void show() {//相當于同步監視器是:this//synchronized (this){if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket);ticket--;}//}} }public class Main3 {public static void main(String[] args) {TicketWindow3 w = new TicketWindow3();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }二、繼承Thread類方式
1、在 run 方法中使用 synchronized 塊
/*** 使用同步代碼塊解決繼承Thread類的方式的線程安全問題* 例子:創建三個窗口賣票,總票數為100張.使用繼承Thread類的方式* 說明:在繼承Thread類創建多線程的方式中,慎用this充當同步監視器,考慮使用當前類充當同步監視器。*/ class TicketWindow2 extends Thread {private static int ticket = 100;private static Object obj = new Object();@Overridepublic void run() {while (true) { // synchronized (obj){//正確的方式1:使用 static 的 obj 作為 synchronized 鎖synchronized (TicketWindow2.class) {//正確的方式2:因為Class clazz = TicketWindow4.class, TicketWindow4.class只會加載一次 // synchronized (this){//錯誤的方式:this代表著t1,t2,t3三個對象if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName() + ":賣票,票號為:" + ticket);ticket--;} else {break;}}}} }public class Main2 {public static void main(String[] args) {TicketWindow2 t1 = new TicketWindow2();TicketWindow2 t2 = new TicketWindow2();TicketWindow2 t3 = new TicketWindow2();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }2、使用syncronized靜態方法
/*** 使用同步方法處理繼承Thread類的方式中的線程安全問題*/ class TicketWindow4 extends Thread {private static int ticket = 100;@Overridepublic void run() {while (true) {show();}}private static synchronized void show() { //正確:使用靜態方法,相當于同步監視器:TicketWindow4.class,是當前類本身 // private synchronized void show(){ //錯誤:使用非靜態方法,相當于同步監視器:t1,t2,t3,是三個不同的對象當做鎖if (ticket > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket);ticket--;}} }public class Main4 {public static void main(String[] args) {TicketWindow4 t1 = new TicketWindow4();TicketWindow4 t2 = new TicketWindow4();TicketWindow4 t3 = new TicketWindow4();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();} }三、線程安全的懶漢式單例模式
/*** 使用同步機制,將單例模式中的'懶漢式'改寫為線程安全的*/ public class Bank {private static Bank bankInstance = null;public static Bank getBankInstance() {if (null == bankInstance) {//進入synchronized前先判斷,效率更高synchronized (Bank.class) {if (null == bankInstance) {bankInstance = new Bank();}}}return bankInstance;} }四、死鎖問題示例
public class LockedTest {public static void main(String[] args) {StringBuffer sb1 = new StringBuffer();StringBuffer sb2 = new StringBuffer();new Thread() {@Overridepublic void run() {synchronized (sb1) {//拿到sb1鎖sb1.append("a");sb2.append("b");System.out.println("拿到sb1鎖");try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("等待sb2鎖");synchronized (sb2) {sb1.append('1');sb2.append('2');System.out.println("sb1=" + sb1);System.out.println("sb2=" + sb2);}}}}.start();new Thread(new Runnable() {//匿名Runnable對象,實現Runnable接口@Overridepublic void run() {synchronized (sb2) {System.out.println("拿到sb2鎖");sb1.append("c");sb2.append("d");System.out.println("等待sb1鎖");synchronized (sb1) {sb1.append('3');sb2.append('4');System.out.println("sb1=" + sb1);System.out.println("sb2=" + sb2);}}}}).start();} }輸出
拿到sb1鎖
拿到sb2鎖
等待sb2鎖
總結
以上是生活随笔為你收集整理的【Java多线程】实现Runnable接口方式 / 继承Thread类方式;使用synchronized锁实现线程安全;线程安全的懒汉式单例模式;死锁问题示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python】多线程的使用,通过传参接
- 下一篇: 【Redis】新浪微博与微信Redis架