java中一個比較著名的問題就是生產消費問題,就是一邊生產出產品,另一邊就消費掉剛剛生產出來的產品,這里面需要用到線程同步和信號量的問題。
    線程同步就是實現線程安全的一種手段,是處理資源共享問題的必須手段。假如多個線程去共享同一個資源,肯定會發生我們意想不到的問題,使得線程并不安全,例如重復取出相同資源。
    而使用信號量是控制各個線程之間調用順序的方法,通過信號量讓線程不斷休息和喚醒,這樣來實現我們想要的功能。
    下面看一個不加線程同步和信號量的程序:
class Person{
    
private String name ;
    
private String sex ;
    
public void set(String name,String sex){
        
this.name = name;
        
this.sex = sex;
    }

    
public String get(){
        
return this.name+"----"+this.sex;
    }

}


class Pro implements Runnable{

    Person p 
=null;
    
public Pro(Person p){
        
this.p = p;
    }

    
public void run() {
        
int i=0;
        
while(true){
            
if(i==0){
                p.set(
"林嘉綺""");
                i 
= 1;
            }

            
else{
                p.set(
"香香""");
                i 
= 0;
            }

        }

    }

}


class Cus implements Runnable{

    Person p 
= null;
    
public Cus(Person p){
        
this.p = p;
    }

    
public void run() {
        
while(true){
            System.out.println(p.get());
            
try {
                Thread.sleep(
1000);
            }
 catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

}

public class Test {

    
public static void main(String[] args) {
        Person p 
= new Person();
        Pro pro 
= new Pro(p);
        Cus cus 
= new Cus(p);
        
new Thread(pro).start();
        
new Thread(cus).start();

    }


}

這個程序的運行結果是:

上面那個程序會產生兩種意外:一個是當只填入一個人的姓名而未填入這個人的性別時,消費者線程就把這個人的姓名和上個人的性別聯系起來并打印輸出;另一個是生產者生產多次產品,而消費者才剛開始取出一個產品,或者是未等到生產者生產出來新產品,而消費者線程就取出多次舊產品。

這樣,我們先加上線程同步方法:在需要鎖定的資源上加上synchronized關鍵字就能保證線程同步。
附代碼:
class Person{
    
private String name ;
    
private String sex ;
    
public synchronized void set(String name,String sex){
        
this.name = name;
        
this.sex = sex;
    }

    
public synchronized String get(){
        
return this.name+"----"+this.sex;
    }

}


class Pro implements Runnable{

    Person p 
=null;
    
public Pro(Person p){
        
this.p = p;
    }

    
public void run() {
        
int i=0;
        
while(true){
            
if(i==0){
                p.set(
"林嘉綺""");
                i 
= 1;
            }

            
else{
                p.set(
"香香""");
                i 
= 0;
            }

        }

    }

}


class Cus implements Runnable{

    Person p 
= null;
    
public Cus(Person p){
        
this.p = p;
    }

    
public void run() {
        
while(true){
            System.out.println(p.get());
            
try {
                Thread.sleep(
1000);
            }
 catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }

}

public class Test {

    
public static void main(String[] args) {
        Person p 
= new Person();
        Pro pro 
= new Pro(p);
        Cus cus 
= new Cus(p);
        
new Thread(pro).start();
        
new Thread(cus).start();

    }


}

這個程序的運行結果是:

上面這個程序解決了第一個問題,即當只填入一個人的姓名而未填入這個人的性別時,消費者線程就把這個人的姓名和上個人的性別聯系起來并打印輸出的問題,但是并不能解決第二個問題。若要是想解決第二個問題,我們就要添加一個信號量,控制線程之間的調用順序。
附代碼:

public class Food{

    
private String name;
    
private String flag;
    
boolean b = true;
    
//為true則允許生產不允許消費
    
//為false則允許消費不允許生產
    public String getName() {
        
return name;
    }

    
public void setName(String name) {
        
this.name = name;
    }

    
public String getFlag() {
        
return flag;
    }

    
public void setFlag(String flag) {
        
this.flag = flag;
    }

    
    
public synchronized void set(String name,String flag){
        
if(!b){
            
try {
                wait();
            }
 catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        
this.setName(name);
        
this.setFlag(flag);
        b 
= false;
        notify();
    }

    
public synchronized String get(){
        
if(b){
            
try {
                wait();
            }
 catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        b 
= true;
        notify();
        
return "名稱:"+this.getName()+"    類型:"+this.getFlag();
    }

}

public class Producer implements Runnable{

    
private Food food;
    
public Producer(Food food){
        
this.food = food;
    }

    
public void run() {

        
boolean b = true;
        
while(true){
            
if(b){
                food.set(
"蘋果""水果");
                b 
= false;
            }

            
else{
                food.set(
"茄子""蔬菜");
                b 
= true;
            }

            
try {
                Thread.sleep(
1000);
            }
 catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }


}


public class Customer implements Runnable{

    
private Food food;
    
public Customer(Food food){
        
this.food = food;
    }

    
public void run() {

        
while(true){
            System.out.println(food.get());
            
try {
                Thread.sleep(
1000);
            }
 catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }


    
}

public class Test {

    
public static void main(String[] args) {

        Food food 
= new Food();
        Producer producer 
= new Producer(food);
        Customer customer 
= new Customer(food);
        
new Thread(producer).start();
        
new Thread(customer).start();
    }


}


我們看看運行結果:


綜上所述,我們要想處理線程生產和消費的問題就需要進行兩步操作:
    1.線程同步:進行資源鎖定,避免各個數據之間不匹配的問題。
    2.加入信號量:通過類似于開關似的信號量來控制各個線程的調度,解決了一個線程連續多次被調用的問題。