非線程安全
public class SafeThread {
public static void main(String[] args) {
Safe12306 t=new Safe12306(100);
Thread thread1=new Thread(t,"小紅");
Thread thread2=new Thread(t,"小白");
Thread thread3=new Thread(t,"小黑");
thread1.start();
thread2.start();
thread3.start();
}
}
class Safe12306 implements Runnable
{
private Integer tickets;
private boolean flag=true;
public Safe12306(Integer tickets) {
this.tickets = tickets;
}
@Override
public void run() {
while (flag)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
BuyTicket();
}
}
private void BuyTicket() {
if (tickets<=0)
{
flag=false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" --> Ticket"+tickets--);
}
}
模擬了延時之后吻贿,可能會出現(xiàn)多個人買到同一張票或者有人買到了不存在的票的情況巍糯。
這就是線程不安全的情況。
為什么會出現(xiàn)這兩種情況傲隶,首先看有人買到不存在的票的情況,當(dāng)只有最后一張票的時候窃页,小黑小紅小白三人都進(jìn)入了BuyTicket方法此時TICKET=1跺株,此時執(zhí)行sleep方法,模擬網(wǎng)絡(luò)延遲脖卖,小黑先進(jìn)入先sleep乒省,到了小紅小紅也sleep,然后小白也sleep畦木,然后小黑重新被分配CPU獲得最后一票袖扛,而小紅和小白只能獲得不存在的票。
多人買到同一張票的情況馋劈,線程的機(jī)制是從主存中復(fù)制數(shù)據(jù)到自己的工作區(qū)攻锰,然后再把自己更改過的內(nèi)容覆蓋到主存晾嘶,多人買到同一張票應(yīng)該是小黑復(fù)制到了自己的工作區(qū)還沒有覆蓋主存,小紅和小白就已經(jīng)復(fù)制主存中的內(nèi)容到自己的工作區(qū)娶吞。
線程安全
- 實(shí)現(xiàn)線程同步
1.同步方法
給BuyTicket方法加上synchronized關(guān)鍵字垒迂。
private synchronized void BuyTicket() {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets<=0)
{
flag=false;
return;
}
System.out.println(Thread.currentThread().getName()+" --> Ticket"+tickets--);
}
synchronized看似鎖的是方法實(shí)際是當(dāng)前對象(this)。更改了哪個對象的內(nèi)容就要鎖哪個對象妒蛇,鎖方法的開銷較大机断,一般不推薦鎖方法。
2.同步代碼塊
public class SafeFrame {
public static void main(String[] args) {
HashMap <String ,Integer> map=new HashMap<>();
ConcurrentHashMap<String ,Integer>cmap=new ConcurrentHashMap<>();
LinkedList<String> list=new LinkedList<>();
for (int i=0;i<10000;i++)
{
int finalI = i;
new Thread(()->{
list.add(Thread.currentThread().getName());
synchronized (map){
map.put(Thread.currentThread().getName(), finalI);
}
cmap.put(Thread.currentThread().getName(),finalI);
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("list==>"+list.size());
System.out.println("hashmap==>"+map.size());
System.out.println("concurrenthashmap==>"+cmap.size());
}
}
syncchronized鎖了map绣夺,結(jié)果輸出和本來就是ConcurrentHashMap一樣吏奸,但是List輸出結(jié)果不正確。
取錢操作
public class SafeBank {
public static void main(String[] args) {
Account tt=new Account("TT",100);
Draw xx=new Draw(tt,90,"xx");
Draw t=new Draw(tt,30,"tt");
Thread thread1=new Thread(t);
Thread thread2=new Thread(xx);
thread1.start();
thread2.start();
}
}
class Account
{
String name;
Integer money;
public Account(String name, Integer money) {
this.name = name;
this.money = money;
}
public void setMoney(Integer money) {
this.money = money;
}
}
class Draw implements Runnable
{
Account account;
Integer pocketmoney;
String pocketName;
public Draw(Account account, Integer pocketmoney,String pocketName) {
this.account = account;
this.pocketmoney = pocketmoney;
this.pocketName=pocketName;
}
@Override
public void run() {
if(account.money-pocketmoney<=0)
{
System.out.println("沒錢");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(account.money-pocketmoney);
System.out.println(pocketName+"取走了"+account.name+pocketmoney+"元,還剩"+account.money+"元");
}
}
如果不加同步就會出現(xiàn)余額為負(fù)數(shù)的情況
這時候需要加Synchronized,但是要注意陶耍,此時需要操作的對象是Account而不是this奋蔚,所以不能在run方法上加鎖,應(yīng)該使用synchronized代碼塊烈钞。
public void run() {
synchronized (account)
{
if(account.money-pocketmoney<=0)
{
System.out.println("沒錢");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.setMoney(account.money-pocketmoney);
System.out.println(pocketName+"取走了"+account.name+pocketmoney+"元,還剩"+account.money+"元");
}
}