以前一直沒有養成一個梳理知識的習慣,今天覆習到多線程的知識的,想到的經典的存錢取錢多線程同步機制,今天在這裏梳理下,錯的地方請各位大佬指正
1:多線程同步的方法
1:使用synchronized關鍵字修飾方法和代碼塊(這就是兩種了)
2:使用特殊變量volatitle關鍵字修飾
3:JAVA5.0引入了重入鎖機制
1: 先看不引入鎖我們存錢取錢會發生什麼
1.1 :新建一個銀行類Bank
package testThread;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.w3c.dom.css.Counter;
/**
* 一本正經的假裝銀行
*
*/
public class Bank {
//賬戶餘額
private int sum=0;
/**
* 存錢
* @param money
*/
public synchronized void addMoney(int money) {
sum+=money;
System.out.println(System.currentTimeMillis()+"存進"+money);
}
/**
* 取錢
*/
public void subMoney(int money)
{
if(sum-money<0)
{
System.out.println("餘額不足");
return;
}else {
sum-=money;
System.out.println(System.currentTimeMillis()+"取出"+money);
}
}
/**
* 查詢餘額
*/
public void lookMoney()
{
System.out.println("賬戶餘額"+sum);
}
}
2:建立顧客完成存錢取錢的操作
package testThread;
/**
* 主方法完成.假裝兩個存錢和取錢的的(大佬有錢,一直存取)
*/
public class TestMain {
public static void main(String args[]){
final Bank bank=new Bank();
Thread tadd=new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bank.addMoney(100);
bank.lookMoney();
}
}
});
Thread tsub = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
bank.subMoney(100);
bank.lookMoney();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
tsub.start();
tadd.start();
}
}
1.3:截圖
結果分析:我電腦CPU處理的太快了,一直沒看到真正想要的結果真實的結果類似這個
餘額不足 賬戶餘額:0 餘額不足 賬戶餘額:100 (有一百你不讓我取,這個貨) 1441792010959存進:100 賬戶餘額:100
爲什麼出現這樣的結果呢,這個就是多線程同時操作同一個資源,沒有用同步機制,發生了小問題
2:引入synchronized關鍵字bank類
package testThread; import org.omg.CORBA.PUBLIC_MEMBER; import org.w3c.dom.css.Counter; /** * 假裝銀行 * */ public class Bank { //賬戶餘額 private int sum=0; /** * 存錢 * @param money */ public synchronized void addMoney(int money) { sum+=money; System.out.println(System.currentTimeMillis()+"存進"+money); } /** * 取錢 */ public synchronized void subMoney(int money) { if(sum-money<0) { System.out.println("餘額不足"); return; }else { sum-=money; System.out.println(System.currentTimeMillis()+"取出"+money); } } /** * 查詢餘額 */ public void lookMoney() { System.out.println("賬戶餘額"+sum); } }
在看看運行結果
咳咳,其實是上面的圖...........懂就好(synchronized 當然也可以修飾靜態的方法,不過調用它的時候會封鎖整個類);
3:synchronized修飾的代碼塊
package testThread;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.w3c.dom.css.Counter;
/**
* 假裝銀行
*
*/
public class Bank {
//賬戶餘額
private int sum=0;
/**
* 存錢
* @param money
*/
public void addMoney(int money) {
synchronized(this)
{
sum+=money;
System.out.println(System.currentTimeMillis()+"存進"+money);
}
}
/**
* 取錢
*/
public void subMoney(int money)
{
synchronized(this)
{
if(sum-money<0)
{
System.out.println("餘額不足");
return;
}else {
sum-=money;
System.out.println(System.currentTimeMillis()+"取出"+money);
}
}
}
/**
* 查詢餘額
*/
public void lookMoney()
{
System.out.println("賬戶餘額"+sum);
}
}
結果和上面的一樣
4:使用volatitle關鍵字
package testThread;
/**
* 假裝
*
*/
public class Bank {
//賬戶餘額
private volatile int sum=0;
/**
* 存錢
* @param money
*/
public void addMoney(int money) {
sum+=money;
System.out.println(System.currentTimeMillis()+"存進"+money);
}
/**
* 取錢
*/
public void subMoney(int money)
{
if(sum-money<0)
{
System.out.println("餘額不足");
return;
}else {
sum-=money;
System.out.println(System.currentTimeMillis()+"取出"+money);
}
}
/**
* 查詢餘額
*/
public void lookMoney()
{
System.out.println("賬戶餘額"+sum);
}
}
結果分析
餘額不足 賬戶餘額:0 餘額不足 賬戶餘額:100 1441792010959存進:100 賬戶餘額:100 1441792011960取出:100 賬戶餘額:0 1441792011961存進:100 賬戶餘額:100
結果又tmd不讓取了,,這個是爲啥呢
因爲volatitle不能保證原子操作導致,每次線程要訪問volatitle變量都是從內存中去讀的,而是不是從緩存中讀的,這就保證每個線程訪問的變量是一樣的,這就保證了同步
5:使用重入鎖實現線程同步
啥也不說,老規矩,上代碼
package testThread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Bank{
//賬戶餘額
private int sum=0;
//1.1: 我們需要先申明這個鎖
private Lock lock=new ReentrantLock();
/**
* 存錢
* @param money
*/
public void addMoney(int money) {
//1.2上鎖
lock.lock();
try {
sum+=money;
System.out.println(System.currentTimeMillis()+"存進"+money);
} catch (Exception e) {
// TODO: handle exception
}finally{
//1.3解鎖
lock.unlock();
}
}
/**
* 取錢
*/
public void subMoney(int money)
{
//1.1上鎖
lock.lock();
try {
if(sum-money<0)
{
System.out.println("餘額不足");
return;
}else {
sum-=money;
System.out.println(System.currentTimeMillis()+"取出"+money);
}
} finally{
//接鎖
lock.unlock();
}
}
/**
* 查詢餘額
*/
public void lookMoney()
{
System.out.println("賬戶餘額"+sum);
}
}
結果分析:
和上上圖結果一樣,不貼了,老鐵....
我們一般同步用synchronized,如果你想要用高級一點的,用lock,但別忘了釋放一下,不理會發生死鎖的哦,同學