1. 程式人生 > >java多執行緒的共享變數訪問控制例項

java多執行緒的共享變數訪問控制例項

最近打算去一家電商公司,對於高併發的資料訪問控制有著嚴格的要求,近期打算把多執行緒的知識在好好補一下。
執行緒排程有五個狀態;
開始,可執行,執行,阻塞,死亡。
啟動執行緒有兩種方法。繼承Thread類或則實現Runnable介面,其實Thread類也實現了Runnbale介面並實現了介面中唯一的方法run。但是Thread例項只能啟動一次,無法共享變數,因此Runnable的靈活性更高。Thread(Runnable target)可以去執行Runnable例項的run方法。雖然start方法和run方法執行的內容一樣但是本質是不同的,run方法只是單純的呼叫而已並沒有啟動多執行緒,而start方法可以啟動多執行緒。去檢視jdk原始碼可以發現start有本地native修飾符,這會呼叫系統函式,優化執行效率。
可執行是一個執行緒池,對於JVM一般是從執行緒池裡選擇優先順序高的執行緒搶佔式執行,如果優先順序都一樣,那就看誰運氣好搶到CPU資源。一般可以自己設定優先順序0~10。這裡還有使用者執行緒和守護執行緒,JVM例項會在使用者執行緒結束時退出,而不會等待守護執行緒。
執行態即開始執行該例項的run方法。為了減緩執行可以採用執行緒Thread.sleep(休眠的毫秒數)去給其他執行緒執行機會,但sleep不會放棄物件的鎖,要放棄鎖可以呼叫超類的wait(放棄毫秒數)方法。notify和notifyAll也可以去喚醒。注意,醒來並不一定馬上得到執行的機會,而是放在可執行池裡,讓JVM去排程到執行態。在執行的程式也可以通過yield方法讓本執行緒放到可執行池裡,因此不要依賴這個方法,這個方法可能並沒什麼用。只作為一個參考手段。為了等待該執行緒執行結束,可以使用jion方法。
死亡態:死亡後不能復生。

接下來我來實現一個共享變數的控制例項:
建立一個我的物件,它有成員變數money,用兩個執行緒去執行它,給一個消費金額不斷的去消費,一直消費到0。

主執行緒中的測試:

public static void main(String args[]) throws InterruptedException
    {
        MyObject myObject = new MyObject(10);
        //定義兩個執行緒來共享這個myObject
        Thread t1 = new Thread(myObject,"Thread-----A");
        Thread t2 = new
Thread(myObject,"Thread-----B"); t1.start(); t2.start(); //這裡要用join方法,否則主執行緒在t1,t2執行的過程中可能並行執行,然後返回最後剩下的money,我想要顯示的最終的結果,因此使用join方法是使t1,t2結束。 t1.join(); t2.join(); System.out.println("============主執行緒休眠1秒"); try { Thread.sleep(1000
); Thread.yield();//將主執行緒放到執行緒池,這裡不起作用。t1,t2已經死亡 } catch(Exception e) { e.printStackTrace(); } //判斷最後的共享變數money的值 System.out.println("myObject當中Money最終為:"+myObject.money); }

接下來我們來共享變數的物件,如果沒用控制方法同步

class MyObject implements Runnable
{
    //可以對成員變數加鎖
//  volatile int money = 100;
    int money = 10000;
    //消費金額,用建構函式去初始化
    private int consume;
    //如果這裡不加synchronized方法去控制
    @Override
    public void run() {
        // TODO Auto-generated method stub
        int i = 0;

        while(money > 0)
        {
            System.out.println(Thread.currentThread().getName()+"執行操作"+ (i+1) +"次");
            money = getSumMoney();
            System.out.println("money:"+money);
            i ++;
        }
        }
        private int getSumMoney()
    {
        return this.money - consume;
    }
    //構造方法,消費
    MyObject(int consume)
    {
        this.consume = consume;
    }

    public int getMoney() {
        return money;
    }
}

執行結果:

//省略前面
...
..
.
Thread-----A執行操作810次
money:100
Thread-----A執行操作811次
money:90
Thread-----A執行操作812次
money:80
Thread-----A執行操作813次
money:70
Thread-----A執行操作814次
money:60
Thread-----A執行操作815次
money:50
Thread-----A執行操作816次
money:40
Thread-----A執行操作817次
money:30
Thread-----A執行操作818次
money:20
Thread-----A執行操作819次
money:10
Thread-----A執行操作820次
money:0
Thread-----B執行操作181次
money:-10
============主執行緒休眠1秒
myObject當中Money最終為:-10

最終為-10,因為最後結果出現了問題。
我們加上訪問修飾符synchronized

執行結果:

//....
//..
//.
Thread-----A執行操作994次
money:60
Thread-----A執行操作995次
money:50
Thread-----A執行操作996次
money:40
Thread-----A執行操作997次
money:30
Thread-----A執行操作998次
money:20
Thread-----A執行操作999次
money:10
Thread-----A執行操作1000次
money:0
============主執行緒休眠1秒
myObject當中Money最終為:0

結果算是沒問題了,但是這裡為什麼只有A方法得到了執行呢?我再來分析程式碼,發現是執行緒A佔用了物件的鎖一直沒釋放,讓執行緒B沒有機會執行。為了達到兩個執行緒都能夠執行的目的,再加上wait方法放棄鎖。程式碼如下:

public synchronized void run() {
        // TODO Auto-generated method stub
        int i = 0;

        while(money > 0)
        {
            System.out.println(Thread.currentThread().getName()+"執行操作"+ (i+1) +"次");
            money = getSumMoney();
            System.out.println("money:"+money);
            try
            {
                System.out.println(Thread.currentThread().getName() + "本執行緒放棄鎖");
                wait(50);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }

            i ++;
        }
        }

執行結果:

//....
...
..
.Thread-----A本執行緒放棄鎖
Thread-----B執行操作496次
money:90
Thread-----B本執行緒放棄鎖
Thread-----A執行操作496次
money:80
Thread-----A本執行緒放棄鎖
Thread-----B執行操作497次
money:70
Thread-----B本執行緒放棄鎖
Thread-----A執行操作497次
money:60
Thread-----A本執行緒放棄鎖
Thread-----B執行操作498次
money:50
Thread-----B本執行緒放棄鎖
Thread-----A執行操作498次
money:40
Thread-----A本執行緒放棄鎖
Thread-----B執行操作499次
money:30
Thread-----B本執行緒放棄鎖
Thread-----A執行操作499次
money:20
Thread-----A本執行緒放棄鎖
Thread-----B執行操作500次
money:10
Thread-----B本執行緒放棄鎖
Thread-----A執行操作500次
money:0
Thread-----A本執行緒放棄鎖
============主執行緒休眠1秒
myObject當中Money最終為:0

很好,問題得到了解決,交替執行並保證了資料的正確性。

相關推薦

JAVA執行——共享變數

【轉載】Java多執行緒程式設計:變數共享分析(Thread) 原部落格網址:https://www.cnblogs.com/xudong-bupt/archive/2013/05/22/3087864.html

java執行共享變數訪問控制例項

最近打算去一家電商公司,對於高併發的資料訪問控制有著嚴格的要求,近期打算把多執行緒的知識在好好補一下。 執行緒排程有五個狀態; 開始,可執行,執行,阻塞,死亡。 啟動執行緒有兩種方法。繼承Thread類或則實現Runnable介面,其實Thread類也實現

執行java執行全域性變數共享問題

【自己的總結】 package dc.stock; import dc.stock.policies.StockP1_1_notComIn; /** * 執行策略的【統一的】方法入口; * 之所以單獨列出來,是為了多執行緒的使用; */ public class

python-執行-共享變數問題

import threading sum = 0 loopSum = 1000000 lock = threading.Lock() def Add(): global sum, loopSum for i in range(1,loopSum): loc

java執行資源互斥訪問

java多執行緒——資源互斥訪問 本文首先介紹一下java實現多執行緒的幾種方式,再介紹一下如何實現資源的互斥訪問,最後介紹生產環境中執行緒池的應用。 好,下面上貨。 一、建立執行緒的兩種方式: 1、

java 執行等待、喚醒機制例項

例子: 1、實體類 public class Student {     String name;     int age;     boolean flag = false; // 表示沒有值 } 2、執行緒1 public class SetThread impleme

Java執行程式設計核心技術(二)物件及變數的併發訪問

最近一直在忙比賽,四五個吧,時間有點緊張,部落格也沒時間更新~ 只能忙裡抽閒 本文屬於Java多執行緒程式設計系列的第二篇,旨在分享我對多執行緒程式設計技術的心得與感悟,順便做下筆記。 如果你閱讀完比較感興趣,歡迎關注我,等待更新後續篇章。 本文主要介紹Java多執行緒中的同步,也就是如何在Java語言中

java執行共享變數

目的:簡述java多執行緒的共享變數   共享變數:多個執行緒都會使用到的同一變數。   Q : 為什麼共享變數會造成資料的錯誤呢???       A : 多個執行緒在操作共享變數的時候,不是直接在主記憶體中去操作的。而

Java執行程式設計核心技術】第二章 物件及變數的併發訪問

synchronized關鍵字 sychronized取得的鎖都是物件鎖,而不是把一段程式碼或方法(函式)當做鎖。 鎖重入功能:當一個執行緒得到一個物件鎖後,再次請求可以再次得到該物件的鎖 出現異常,鎖自動釋放 同步不具有繼承性 class Bas

Java執行共享變數&同步-非同步容器&執行區域性變數

共享變數 (Volatile Atomic) volatile:當多個執行緒訪問一個成員變數的時候,需要這個變數在多個執行緒中可見。 Atomic:Atomic方法對該變數的操作是原子性操作,顆粒度是到對這個變數的一次操作。 private stati

(三) Java執行詳解之執行範圍內共享變數及ThreadLocal類使用

執行緒範圍內共享變數 HashTable方式實現 在開發中經常會遇到一種情況:有一個變數會被多個執行緒訪問,但是要確保同個執行緒內訪問的是同一個物件,Hashtable方式實現程式碼如下: public class ThreadExample5 {

Java執行之物件及變數的併發訪問

Java物件及變數的併發訪問 當多個執行緒同時對同一個物件中的例項變數進行併發訪問時可能會產生執行緒安全問題。產生的後果就是”髒讀”,即收到的資料其實是被更改過的。 如果訪問的是方法中的變數,則不存在”非執行緒安全”問題 可以通過以下幾種方式來解決,在對物

Java執行執行訪問共享物件和資料的方式

1.如果每個執行緒執行的程式碼相同,可以使用同一個Runable物件,這個Runable物件中有那個共享資料,例如賣票系統就可以這樣做。 package javaplay.test; public class MulteThreadShareData { publi

java執行:4、執行範圍內的資料共享_公共變數

直接上例項,注意事項已經在程式中做了註釋,以此共勉。加油 import java.util.Map; import java.util.Random; import java.util.concu

Java執行程式設計核心技術--第2章 物件及變數的併發訪問

2.1 synchronized同步方法 方法中的變數不存在非執行緒安全問題,永遠都是執行緒安全的。這是方法內部的變數私有特性造成的。 2.1.2 例項變數非執行緒安全 使用多個執行緒併發訪問PrivateNum類中的addI方法。 publ

java 執行(4) 執行同步之鎖(synchronized) / 死鎖 / 兩個鎖定物件期間訪問(修改)其變數的面試

一. 鎖的定義 鎖就是synchronized 關鍵字,記住synchronized(this )是鎖定當前物件。在函式m1()裡面寫synchronized( this ),這個和public synchronized void m1() 等價。 但是他只鎖定當前物件的s

Java執行執行範圍內共享變數的概念與作用

要實現執行緒範圍內的資料共享,就是說不管是A模組還是B模組,如果它們現在在同一個執行緒上執行,它們操作的資料應該是同一個,下面的做法就不行: package javaplay.thread.test; import java.util.Random; public

Java執行——物件及變數的併發訪問

Java多線系列文章是Java多執行緒的詳解介紹,對多執行緒還不熟悉的同學可以先去看一下我的這篇部落格Java基礎系列3:多執行緒超詳細總結,這篇部落格從巨集觀層面介紹了多執行緒的整體概況,接下來的幾篇文章是對多執行緒的深入剖析。   本篇文章主要介紹Java多執行緒中的同步,也就是如何在Java語

java執行程式設計之使用Synchronized塊同步變數

通過synchronized塊來同步特定的靜態或非靜態方法。 要想實現這種需求必須為這些特性的方法定義一個類變數,然後將這些方法的程式碼用synchronized塊括起來,並將這個類變數作為引數傳入synchronized塊   下面的程式碼演示瞭如何同步特定的類方法:

執行-共享全域性變數

from threading import Thread import time g_num = 100 def work1(): global g_num for i in range(3): g_num += 1 print("----in wo