java中一個比較著名的問題就是生產(chǎn)消費(fèi)問題,就是一邊生產(chǎn)出產(chǎn)品,另一邊就消費(fèi)掉剛剛生產(chǎn)出來的產(chǎn)品,這里面需要用到線程同步和信號量的問題。
線程同步就是實現(xiàn)線程安全的一種手段,是處理資源共享問題的必須手段。假如多個線程去共享同一個資源,肯定會發(fā)生我們意想不到的問題,使得線程并不安全,例如重復(fù)取出相同資源。
而使用信號量是控制各個線程之間調(diào)用順序的方法,通過信號量讓線程不斷休息和喚醒,這樣來實現(xiàn)我們想要的功能。
下面看一個不加線程同步和信號量的程序:
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();

}

}
這個程序的運(yùn)行結(jié)果是:

上面那個程序會產(chǎn)生兩種意外:一個是當(dāng)只填入一個人的姓名而未填入這個人的性別時,消費(fèi)者線程就把這個人的姓名和上個人的性別聯(lián)系起來并打印輸出;另一個是生產(chǎn)者生產(chǎn)多次產(chǎn)品,而消費(fèi)者才剛開始取出一個產(chǎn)品,或者是未等到生產(chǎn)者生產(chǎn)出來新產(chǎn)品,而消費(fèi)者線程就取出多次舊產(chǎn)品。
這樣,我們先加上線程同步方法:在需要鎖定的資源上加上synchronized關(guān)鍵字就能保證線程同步。
附代碼:
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();

}

}
這個程序的運(yùn)行結(jié)果是:

上面這個程序解決了第一個問題,即當(dāng)只填入一個人的姓名而未填入這個人的性別時,消費(fèi)者線程就把這個人的姓名和上個人的性別聯(lián)系起來并打印輸出的問題,但是并不能解決第二個問題。若要是想解決第二個問題,我們就要添加一個信號量,控制線程之間的調(diào)用順序。
附代碼:
public class Food{

private String name;
private String flag;
boolean b = true;
//為true則允許生產(chǎn)不允許消費(fèi)
//為false則允許消費(fèi)不允許生產(chǎn)
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();
}

}
我們看看運(yùn)行結(jié)果:

綜上所述,我們要想處理線程生產(chǎn)和消費(fèi)的問題就需要進(jìn)行兩步操作:
1.線程同步:進(jìn)行資源鎖定,避免各個數(shù)據(jù)之間不匹配的問題。
2.加入信號量:通過類似于開關(guān)似的信號量來控制各個線程的調(diào)度,解決了一個線程連續(xù)多次被調(diào)用的問題。
線程同步就是實現(xiàn)線程安全的一種手段,是處理資源共享問題的必須手段。假如多個線程去共享同一個資源,肯定會發(fā)生我們意想不到的問題,使得線程并不安全,例如重復(fù)取出相同資源。
而使用信號量是控制各個線程之間調(diào)用順序的方法,通過信號量讓線程不斷休息和喚醒,這樣來實現(xiàn)我們想要的功能。
下面看一個不加線程同步和信號量的程序:






























































這個程序的運(yùn)行結(jié)果是:

上面那個程序會產(chǎn)生兩種意外:一個是當(dāng)只填入一個人的姓名而未填入這個人的性別時,消費(fèi)者線程就把這個人的姓名和上個人的性別聯(lián)系起來并打印輸出;另一個是生產(chǎn)者生產(chǎn)多次產(chǎn)品,而消費(fèi)者才剛開始取出一個產(chǎn)品,或者是未等到生產(chǎn)者生產(chǎn)出來新產(chǎn)品,而消費(fèi)者線程就取出多次舊產(chǎn)品。
這樣,我們先加上線程同步方法:在需要鎖定的資源上加上synchronized關(guān)鍵字就能保證線程同步。
附代碼:






























































這個程序的運(yùn)行結(jié)果是:

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










































































































我們看看運(yùn)行結(jié)果:

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