線程安全的問題出現(xiàn)的原因
1.存在兩個(gè)或者兩個(gè)以上的線程
2.共享著同一個(gè)資源并且執(zhí)行語句是多句
** 解決方法 **
第一種 (同步塊代碼)
1.格式
synchronized (鎖對(duì)象){
執(zhí)行的語句
}
2.鎖對(duì)象可以是任意的對(duì)象 (必須是唯一的)
3.鎖對(duì)象必須是共享的數(shù)據(jù),不能為靜態(tài)的成員變量
4.static或者常量池中屬性
第二種(同步函數(shù))
1.非靜態(tài)的同步方法鎖對(duì)象是----this對(duì)象(當(dāng)前的對(duì)象)
2.靜態(tài)的同步方法的鎖對(duì)象是----當(dāng)前類的claas文件 ===>(new Class)
(當(dāng)類的class文件加載進(jìn)入到內(nèi)存的時(shí)候,jvm會(huì)自動(dòng)的生成對(duì)應(yīng)的(Class)描述文件)
** 同步函數(shù)代碼**
//非靜態(tài)的同步函數(shù) ===>鎖對(duì)象===> this對(duì)象
@Override
public synchronized void run() {
System.out.println("非靜態(tài)同步函數(shù)的實(shí)現(xiàn)");
}
//靜態(tài)同步函數(shù) ===> 鎖對(duì)象===> 當(dāng)前方法所屬類的class文件對(duì)象
public synchronized static void saleTitck(){
System.out.println("靜態(tài)同步函數(shù)的實(shí)現(xiàn)");
}
同步代碼塊和同步函數(shù)的區(qū)別
1.同步代碼塊的鎖對(duì)象我們自己指定寿酌,同步函數(shù)是對(duì)象指定
2.同步代碼塊可以隨意指定那個(gè)范圍被同步示弓,同步函數(shù)必須是整個(gè)函數(shù)
3.如果沒有存在線程安全的問題就不需要使用同步代碼塊
4.推薦使用同步代碼塊的使用
賣票的列子 (繼承了Thread類)
/*
賣票的例子:
3個(gè)窗口同時(shí)買票的功能 同時(shí)賣50張票的功能
出現(xiàn)了線程安全的問題:
1.數(shù)據(jù)一定使用共享的數(shù)據(jù)使用static修飾
2.同步代碼塊的鎖對(duì)象也是靜態(tài)的 static 或者常量池中的常量
*/
public class SaleTitick extends Thread {
public SaleTitick(String name){
super(name);
}
//共50張 這個(gè)要共享數(shù)據(jù) 定義為static
static int titick = 50;
//這個(gè)鎖對(duì)象一定鑰匙靜態(tài)的,才可以統(tǒng)一的管理
static Object o = new Object();
@Override
public void run() {
while (true){
//同步的代碼塊的方式 鎖對(duì)象是任何的對(duì)象啃沪,任何對(duì)象都內(nèi)置了隱式的一個(gè)狀態(tài) 這個(gè)狀態(tài)就是記錄鎖的狀態(tài)了
synchronized ("鎖"){
if (titick>0){
System.out.println(Thread.currentThread().getName()+"賣出第"+titick+"張票");
titick--;
}else{
System.out.println("票賣完了");
break;
}
}
}
}
public static void main(String[] args) {
SaleTitick saleTitick1 = new SaleTitick("窗口1");
SaleTitick saleTitick2 = new SaleTitick("窗口2");
SaleTitick saleTitick3 = new SaleTitick("窗口3");
//調(diào)用start方法
saleTitick1.start();
saleTitick2.start();
saleTitick3.start();
}
}
賣票列子(實(shí)現(xiàn) Runable接口)
/**
需求:
賣50張票的實(shí)現(xiàn)了Runable類
*/
public class SaleTitckRunable implements Runnable {
static int titck = 50; //靜態(tài)數(shù)據(jù)共享
static Object o = new Object(); //鎖對(duì)象共享
@Override
public void run() {
while (true){
//同步代碼塊的方法去實(shí)現(xiàn)這個(gè)功能
synchronized (o){
if (titck>0){
System.out.println(Thread.currentThread().getName()+":"+titck);
titck--;
}else{
System.out.println("賣完了");
break;
}
}
}
}
public static void main(String[] args) {
SaleTitckRunable runable = new SaleTitckRunable();
SaleTitckRunable runable1 = new SaleTitckRunable();
SaleTitckRunable runable2 = new SaleTitckRunable();
Thread thread = new Thread(runable,"線程1");
Thread thread1 = new Thread(runable1 ,"線程2");
Thread thread2 = new Thread(runable2,"線程3");
thread.start();
thread1.start();
thread2.start();
}
}
死鎖出現(xiàn)的現(xiàn)象 (兩個(gè)線程都互相同步枪孩,你不讓我我也不讓你)
** 代碼如下**
/**
死鎖出現(xiàn)的問題:
需求:
天氣熱,去辦公室拿遙控和電池
zhangsan先去拿遙控器,再去拿電池
lisi先去拿電池,然后再去拿遙控器
*/
public class DeadLock extends Thread{
public DeadLock(String name){
super(name);
}
//重寫run方法
@Override
public void run() {
while (true){
if ("zhangsan".equals(this.getName())){
synchronized ("遙控器"){
System.out.println(this.getName()+"拿到了遙控器,接下來去拿電池");
}
synchronized ("電池"){
System.out.println(this.getName()+"拿到了電池真開心");
}
}else if ("lisi".equals(this.getName())){
synchronized ("電池"){
System.out.println(this.getName()+"拿到了電池,接下來去拿遙控器");
}
synchronized ("遙控器"){
System.out.println(this.getName()+"拿到了遙控器真開心");
}
}
}
}
public static void main(String[] args) {
DeadLock deadLock = new DeadLock("zhangsan");
DeadLock deadLock1 = new DeadLock("lisi");
deadLock.start();
deadLock1.start();
}
}