如下三種狀況都是在須要生產出一個,立刻就消費一個的狀況下:安全
單生產單消費:多線程
單生產單消費是個簡單的多線程,只需保證多線程同步,而且鎖爲同一把鎖便可。代碼體現:函數
class Demo{this
private Object obj=new Object(); //建立鎖對象obj線程
private int count=0;對象
private boolean flag=false; //建立標記接口
public void set(){get
synchronized(obj){ //同步線程同步
while(flag)產品
try {
obj.wait(); //線程等待
} catch (InterruptedException e) {
}
count++;
flag=true;
System.out.println("生產的產品"+count);
obj.notify(); //喚醒線程
}
}
public void sell(){
synchronized (obj) {
while(!flag)
try {
obj.wait();
} catch (InterruptedException e) {}
flag=false;
System.out.println("消費的產品"+count);
obj.notify();
}
}
}
class Set implements Runnable{
private Demo d;
public Set(Demo d){
this.d=d;
}
public void run(){
while(true){
d.set();
}
}
}
class Sell implements Runnable{
private Demo d;
public Sell(Demo d){
this.d=d;
}
public void run(){
while(true){
d.sell();
}
}
}
public class TextThread01 {
public static void main(String[] args) {
Demo d=new Demo();
Set st=new Set(d);
Sell sl=new Sell(d);
Thread t1=new Thread(st);
Thread t2=new Thread(sl);
t1.start();
t2.start();
}
}
以上是整個單生產單消費的程序,重點:1 保證鎖必須爲同一個,2線程等待喚醒的判斷邏輯沒問題。單生產多消費並不複雜.
多生產多消費和單生產和多消費就比單生產單消費複雜點了,按照上面的代碼的邏輯來處理多生產多消費的話,會出現多線程的安全問題。下面分析緣由:
先說單生產多消費的線程運行:生產線程1運行喚醒語句後可能還有執行權,而後繼續判斷,這時的flag爲true,生產線程1便進入等待。喚醒線程照樣執行,只是沒有意義。而後,這時有2個活的消費線程,其中一個啓動,運行結束後一樣可能有執行權,判斷(這時flag爲false),消費線程1就進入等待,交出鎖,由於這裏只有生產線程處於等待狀態,因此就喚醒線程1.到這裏,生產線程1和消費線程2都有執行資格,若是消費線程2拿到鎖,進入判斷,又進入等待,把鎖交出,只有生產線程1能夠拿到,生產線程1拿到後運行後進入等待,喚醒消費線程中的任意一個(這裏假設喚醒消費線程1),消費線程拿到執行權,執行完等待。這是有2個線程在等待,若是消費線程1喚醒的是消費線程2,這裏的falg是false,線程2就進入等待,這裏沒有喚醒任何線程,就出現了死鎖。因此單生產多消費用這樣的代碼實現是會出現安全問題的。
處理方案:將程序中的喚醒語句obj.notify換成obj.notifyAll能夠解決安全問題,推導參照上述推導便可。換成所有喚醒後,線程的運行的效率就會變得很低。要解決這裏的效率問題,須要用到jdk1.5新推出的locke類和其子類condition接口。lock類至關於鎖,condition至關於監視器。lock類相較於同步來講,優點在於能夠在一個鎖內建立2個監視器。
代碼體現:
class Demo{
private Object obj=new Object();
private int count=0;
private boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition cot=lock.newCondition();
private Condition col=lock.newCondition();
public void set(){
//synchronized(obj){
lock.lock();try{
while(flag)
try {
cot.await();
} catch (InterruptedException e) {
}
count++;
flag=true;
System.out.println("生產的產品"+count);
col.signal();
//}
}finally{
lock.unlock();
}
}
public void sell(){
lock.lock();
try{
//synchronized (obj) {
while(!flag)
try {
col.await();
} catch (InterruptedException e) {}
flag=false;
System.out.println("消費的產品"+count);
cot.signal();
//}
}finally{
lock.unlock();
}
}
}
class Set implements Runnable{
private Demo d;
public Set(Demo d){
this.d=d;
}
public void run(){
while(true){
d.set();
}
}
}
class Sell implements Runnable{
private Demo d;
public Sell(Demo d){
this.d=d;
}
public void run(){
while(true){
d.sell();
}
}
}
public class TextThread01 {
public static void main(String[] args) {
Demo d=new Demo();
Set st=new Set(d);
Sell sl=new Sell(d);
Thread t1=new Thread(st);
Thread t2=new Thread(sl);
Thread t3=new Thread(sl);
t1.start();
t2.start();
t3.start();
}
}
多消費多生產和單生產單消費的機制是同樣的,只需再建立一個生產對象便可,代碼體現如上。
守護線程:
將線程定位守護線程後,該線程的特性爲:當該多線程中只剩下守護線程在運行時,守護線程就中止運行。代碼體現:
class Daemon extends Thread{
private int count=0;
public void print(){
System.out.println(Thread.currentThread().getName()+"***********"+count);
count++;
}
public void run(){
for(int i=0;i<1000;i++){
print();
}
}
}
public class TextDaemon01 {
public static void main(String[] args) {
//TextDaemon01 td=new TextDaemon01();
Daemon d=new Daemon();
Daemon d1=new Daemon();
d1.start();
d.setDaemon(true);
//d.setDaemon(true);
d.start();
}
}
這裏有個疑問:主函數能不能做爲守護線程?
線程優先級:
getPriority是返回線程的優先級,setPriority爲設置線程的優先級。線程優先級爲0-10級,咱們設置的時候爲了能夠獲得明顯的線程優先效果,通常設置爲MAX_PRIORITY,MINPRIORITY,NORM_PRIORITY.
Join yield方法:
Join方法的意思:線程2是加入一個線程1,線程1將釋放執行全,等線程2執行完畢後,線程1纔開始繼續運行。先看代碼體現:
public class TextDaemon01 {
public static void main(String[] args) throws InterruptedException {
//TextDaemon01 td=new TextDaemon01();
Daemon d=new Daemon();
Daemon d1=new Daemon();
d.start(); //語句1
d.join();//語句2
d1.start();//語句3
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
語句123如今這個狀況就是主函數線程運行到語句1時,線程1啓動,語句2時主函數線程開始等待線程1的執行完畢,再和線程2搶奪執行權開始運行。
d.start(); //語句1
d1.start();//語句3
d.join();//語句2
當語句123是上面的狀況時,線程12交替運行,等線程1運行完畢後主函數線程纔開始運行。這裏說明一點:主線程釋放執行權不是指向的,活線程均可以搶奪執行權並執行。
Yield:
這個方法的意思:讓線程暫時暫停,可是該線程可能獲取到執行權。
線程在匿名內部類的的表現:
代碼體現:
public class TextThread02 {
public static void main(String[] args) {
new Thread(){
public void run(){
while(true){
System.out.println("......");
}
}
}.start();;
}
}
就是將線程寫入匿名內部類,功能沒變化。