指的是多個線程同時修改一個過程時,可能導致的問題
引入:
以賣票問題爲例:假設總共有十張票,三個黃牛賣票(代表三個子線程),預測最後一個黃牛賣完票,還剩下0張票
package se.SE.practice;
class MyThread implements Runnable {
private int ticket=10;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (this.ticket > 0) {
try {
// 模擬網絡延遲
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "還剩下:" + this.ticket-- + "票");
}
}
}
}
public class Thread1{
public static void main(String[] args) {
MyThread mt=new MyThread();
Thread thread1=new Thread(mt,"黃牛1");
Thread thread2=new Thread(mt,"黃牛2");
Thread thread3=new Thread(mt,"黃牛3");
thread1.start();
thread2.start();
thread3.start();
}
}
執行結果:
我們發現最後一個黃牛賣票還剩下1張票,和預期不符
爲什麼會出現這個問題呢?是因爲在多線程併發執行的時候,發生了同步的問題,也就是多個線程同時訪問同一塊資源(ticket)
解決思路:
在黃牛線程訪問ticket時,不允許有其他線程訪問ticket
引入:synchronized關鍵字
Object someObject =new Object();
synchronized (someObject){
//此處的代碼只有佔有了someObject後纔可以執行
}
賣完票才解鎖:在for循環後加synchronized(this){}
也就是在任何一個時刻內只有一個線程在賣票,
三個線程(黃牛1,黃牛2,黃牛3):可以同時進入for循環和run()但是不可同時進入synchroniazed鎖住的代碼塊
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (this) {
//---------------------------
if (this.ticket > 0) {
try {
// 模擬網絡延遲
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "還剩下:" + this.ticket-- + "票");
}
}
//-------------------------------------------------
}
}
1同步問題:
a.同步代碼塊
在方法中使用synchronized對象,一般可以鎖定當前this
表示同一時刻只有一個線程可以進入同步代碼塊,但是多個線程可以同時進入方法
b.同步方法
在方法聲明中加入synchronized,表示此時只有一個線程可以進入方法
2synchronized對象鎖
觀察以下代碼:
1定義一個synchronized方法,鎖住當前對象this
2第一次new了一個Syn對象syn1,記線程0,那麼線程0
線程1啓動創建Syn對象syn2,各自鎖各自的對象
package se.SE.practice;
class Syn{
public synchronized void test(int ticket){
System.out.println(Thread.currentThread().getName()+"此方法開始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"此方法結束");
}
}
class MyThread implements Runnable {
private int ticket = 10;
@Override
public void run() {
Syn syn=new Syn();
syn.test(ticket);
}
}
public class Thread1{
public static void main(String[] args) {
MyThread myThread=new MyThread();
Thread thread0=new Thread(myThread,"子線程0");
Thread thread1=new Thread(myThread,"子線程1");
Thread thread2=new Thread(myThread,"子線程2");
thread0.start();
thread1.start();
thread2.start();
/*for(int i=0;i<3;i++){
new Thread(myThread,"線程"+i).start();
}
*/
}
}
結果:
總結:用synchronized(this)以及synchronized()只能防止多個線程執行同一個對象的同步段
synchronized鎖的是括號中的對象而非代碼
那麼怎樣纔可以使得可以鎖住一個對象呢,關鍵點在MyThread類實現Runnable 接口中只有一個對象syn
1定義Syn類對象syn
2通過有參構造函數實現對MyThread類對象個數的限制
package se.SE.practice;
class Syn{
public synchronized void test(int ticket){
System.out.println(Thread.currentThread().getName()+"此方法開始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"此方法結束");
}
}
class MyThread implements Runnable {
private int ticket = 10;
private Syn syn;
public MyThread(Syn syn){
this.syn=syn;
}
@Override
public void run() {
this.syn.test(ticket);
}
}
public class Thread1{
public static void main(String[] args) {
Syn syn=new Syn();
MyThread myThread=new MyThread(syn);
for(int i=0;i<3;i++){
new Thread(myThread,"線程"+i).start();
}
}
}
輸出結果:
達到預期結果!
引入全局鎖,從sychronized(this)或者synchronized方法只可以鎖住當前對象,鎖住代碼段需要全局鎖
3全局鎖
鎖代碼段
1使用類的靜態方法,此時鎖住的是當前使用的類,而非對象
也就是把public synchronized void test(int ticket){}方法改爲靜態方法--->public static synchronized void test(int ticket){}
把類方法鎖住--就把代碼塊鎖住
package se.SE.practice;
class Syn{
public synchronized void test(int ticket){
System.out.println(Thread.currentThread().getName()+"此方法開始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"此方法結束");
}
}
class MyThread implements Runnable {
private int ticket = 10;
private Syn syn;
@Override
public void run() {
this.syn.test(ticket);
}
}
public class Thread1{
public static void main(String[] args) {
MyThread myThread=new MyThread();
for(int i=0;i<3;i++){
new Thread(myThread,"線程"+i).start();
}
}
}
2在同步代碼塊中,鎖當前的class對象,類名稱.class
如果是鎖普通方法:將普通方法的代碼段放在synchronized(Syn.class){}包含的代碼段中