7.線程的同步和鎖
為什么要進(jìn)行線程的同步薪者?
Java是允許多線程,當(dāng)多個(gè)線程操作同一個(gè)資源的時(shí)候玉吁,會(huì)導(dǎo)致得到或者打印的數(shù)據(jù)不準(zhǔn)確照弥。從而發(fā)生沖突。咋解決进副?加同步鎖这揣。
package com.wyx.c_suo;
//賣(mài)車(chē)票
class MySycn implements Runnable{
int ticket = 50;
@Override
public void run() {
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣(mài)了第:" + ticket + "張票");
ticket--;
}else {
System.out.println("賣(mài)完了");
break;
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
MySycn mySycn = new MySycn();
//多個(gè)線程操作同一資源,三個(gè)線程都去賣(mài)票
new Thread(mySycn,"MySycn1線程").start();
new Thread(mySycn,"MySycn2線程").start();
new Thread(mySycn,"MySycn3線程").start();
}
}
從運(yùn)行結(jié)果可以看出,第50張票三個(gè)線程都賣(mài)了给赞,不合法机打,正常生活邏輯應(yīng)該是只有一個(gè)線程在賣(mài)第50張這張票
解決方案:1.同步方法:使用一個(gè)關(guān)鍵字synchronized修飾run方法。因?yàn)镴ava對(duì)象都有一個(gè)內(nèi)置的鎖對(duì)象塞俱。當(dāng)使用這個(gè)關(guān)鍵字的時(shí)候姐帚,修飾方法的時(shí)候吏垮,這個(gè)方法就會(huì)被鎖保護(hù)起來(lái)被鎖鎖住
當(dāng)一個(gè)線程進(jìn)來(lái)以后障涯,會(huì)立馬鎖住當(dāng)前的方法。意味著只有一個(gè)線程進(jìn)來(lái)膳汪,其他線程都在外面等著唯蝶。
語(yǔ)法格式:@Override public synchronized void run() { }
會(huì)導(dǎo)致一種結(jié)果:假如線程1搶到了cpu,線程1會(huì)把循環(huán)執(zhí)行完遗嗽,也就是票賣(mài)完才會(huì)解開(kāi)鎖粘我,這個(gè)時(shí)候線程2搶到cpu,發(fā)現(xiàn)票已經(jīng)賣(mài)完痹换,直接就跳出了征字,線程3也一樣,會(huì)出現(xiàn)一家獨(dú)大的這種情況娇豫,也是不符合生活場(chǎng)景的,說(shuō)明不能在方法中加鎖匙姜,需要在其它地方加鎖。
換另外一種解決方法:
2.同步代碼塊:就是使用synchronized 關(guān)鍵字修飾一個(gè)語(yǔ)句塊冯痢。被修飾的語(yǔ)句塊會(huì)被加鎖氮昧。從而實(shí)現(xiàn)同步。
語(yǔ)法格式:synchronized (this) { }
第一種方法浦楣,同步方法
package com.wyx.c_suo;
//賣(mài)車(chē)票
class MySycn1 implements Runnable{
int ticket = 50;
@Override
public synchronized void run() {
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣(mài)了第:" + ticket + "張票");
ticket--;
}else {
System.out.println("賣(mài)完了");
break;
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
MySycn1 mySycn1 = new MySycn1();
//多個(gè)線程操作同一資源袖肥,三個(gè)線程都去賣(mài)票
new Thread(mySycn1,"MySycn1線程").start();
new Thread(mySycn1,"MySycn2線程").start();
new Thread(mySycn1,"MySycn3線程").start();
//使用一個(gè)關(guān)鍵字synchronized修飾run方法
//假如線程1搶到了cpu,線程1會(huì)把循環(huán)執(zhí)行完振劳,也就是票賣(mài)完才會(huì)解開(kāi)鎖椎组,這個(gè)時(shí)候線程2搶到
//cpu,發(fā)現(xiàn)票已經(jīng)賣(mài)完历恐,直接就跳出了庐杨,線程3也一樣,會(huì)出現(xiàn)一家獨(dú)大的這種情況夹供,也是不符合
//生活場(chǎng)景的
}
}
第二種方法灵份,同步代碼塊
package com.wyx.c_suo;
//賣(mài)車(chē)票
class MySycn2 implements Runnable{
int ticket = 50;
@Override
public void run() {
//不能對(duì)循環(huán)加鎖,如果要是對(duì)循環(huán)加鎖哮洽,循環(huán)完才會(huì)釋放鎖填渠,還是出現(xiàn)一家獨(dú)大現(xiàn)象,
//應(yīng)當(dāng)對(duì)if語(yǔ)句加鎖
while (true) {
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣(mài)了第:" + ticket + "張票");
ticket--;
}else {
System.out.println("賣(mài)完了");
break;
}
}
}
}
}
public class Demo3 {
public static void main(String[] args) {
MySycn2 mySycn2 = new MySycn2();
//多個(gè)線程操作同一資源,三個(gè)線程都去賣(mài)票
new Thread(mySycn2,"MySycn1線程").start();
new Thread(mySycn2,"MySycn2線程").start();
new Thread(mySycn2,"MySycn3線程").start();
}
}
案例:
加鎖的目的為了保證數(shù)據(jù)的準(zhǔn)確性氛什。
賣(mài)電影票:
三個(gè)線程:
淘票票
美團(tuán)
貓眼
總共有100張票莺葫,三家去賣(mài)
package com.wyx.c_suo;
class SaleTicket implements Runnable{
//聲明一個(gè)變量,代表電影票當(dāng)前剩余量枪眉。
private static int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣(mài)了第" + ticket + "張票");
ticket--;
}else {
System.out.println("賣(mài)完了");
break;
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
SaleTicket saleTicket = new SaleTicket();
new Thread(saleTicket,"淘票票").start();
new Thread(saleTicket,"美團(tuán)").start();
new Thread(saleTicket,"貓眼").start();
}
}