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.加入信号量:通过类似于开关似的信号量来控制各个线程的调度,解决了一个线程连续多次被调用的问题。