黑马程序员----3多线程

------- android培训java培训、期待与您交流! ----------

 

 

>>进程是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

 

线程是进程中的内容。是进程中真正执行的部分,是进程中得一个独立的控制单元,控制着进程的执行。所以一个进程这种至少有一个线程。

 

Java虚拟机启动的时候会有一个进程java.exe。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。

 

jvm的启动其实就是多线程程序。其中有一个程序负责从主函数开始执行,并控制程序运行的流程。同时为了提高效率,还启动了另一个控制单元(执行路径)专门负责堆内存中的垃圾回收。在程序正常执行过程中,如果出现了垃圾,这时另一个负责收垃圾的线程会在不定时的时间进行垃圾的处理。这两个程序是同时执行的。

 

负责垃圾回收代码执行的线程,称为垃圾回收线程,该线程要执行的代码在finalize中。

 

>>javalang包中的类Thread可以定义线程。

创建线程的第一种(下面还有第二种)方式:继承Thread

1、定义类继承Thread

2、覆盖Thread类中的run方法。

3、创建Thread类的子类对象创建线程。

4、调用Thread类中的start方法开启线程,并执行子类中的run方法。

 

特点:当类去描述事物,事物中有属性和行为。如果行为中有部分代码需要被多线程所执行,如果行为中有部分代码需要被多线程所执行,就需要该类继承Thread类,产生该类的对象作为线程对象。可是这样做会导致每一个对象中都存储一份属性数据。无法在多个线程中共享

egclass Demo extendsThread//定义类继承Thread

{

         publicvoid run()//run()方法定义于Thread中,覆盖

{

System.out.println(“Demo run”);

         }

}

 

class ThreadDemo

{

         publicstatic void main(String[] args)//主线程是一个控制单元

         {

                   Demod = new Demo();//创建好一个线程,一个控制单元

                   d.start();//start()方法定义于Thread中,功能是启动线程和调用run()方法

}

}

 

>>eg:多线程交替执行的情况

class Demo extends Thread

{

         publicvoid run()

{

for(int x=0; x<60;x++)

{System.out.println(“Demo run”+x);}

         }

}

 

class ThreadDemo

{

         publicstatic void main(String[] args)

         {

                   Demod = new Demo();//Demo线程

                   d.start();

 

                   for(intx=0;x<60;x++)//主线程中执行

                            {Systme.out.println(“main”+x);}

}

}

执行结果:会出现“mainx”与“Demo runx”交替打印的现象。cpu在运行时切换进程中的线程。当执行到Demo d = new Demo();时,除了主线程,还增加了一个d线程。

 

>>run();方法用于存储代码,一般需要覆盖,否则调用的是Thread中得run方法,里面是没有东西可以用的。另外,开启线程的目的是为了运行自己的代码,所以覆盖。通过start方法调用run方法。

 

如果直接调用run方法可以么?不可以,虽然运行正常,但是start的功能是开启线程并执行线程的run方法。run仅仅是对象调用方法。如果直接调用run方法,则线程虽然创建了,但是没有执行,执行run方法的还是主线程。

 

>>线程对象的获取和名称的定义

线程的编号是从0开始的

1String getName()//返回该线程的名称。

但是主函数没有继承Thread不能直接使用getName(),而且主线程并非程序员定义的!

通过Thread中一个方法调用:

staticThread currentThread()

返回对当前正在执行的线程对象的引用。

eg:…………main(String[]args)

{

         ………………

         System.out.println(Thread.currentThread().getName()+”--”+x);

         ………………

}

 

2、通过Thread构造函数可以给线程命名

egclass Demo extendsThread

{

         Demo(Stringname)

         {

                   super(name);//传给父类的构造方法

         }

         publicvoid run()//覆盖父类的run方法

{

for(int x=0; x<60;x++)

{System.out.println(“Demo run”+x);}

         }

}

 

3、如果要改名呢?

void setName(String name)//改变线程名称,使之与参数name相同。

eg:………………

         Demod = new Demo(“旺财”)

         d.setName(“小强”);

         ………………//线程名为“小强”而不是“旺财”或Thread-0

 

>>线程被创建后有运行状态、冻结状态、消亡状态、临时阻塞状态(有资格无权)。

         假设有三个线程ABC。当三个都start()方法后,叫做三个线程具备了执行资格。处于临时阻塞状态。

         当某一个线程A正在被cpu处理,说明A处于运行状态,即具备了资格,也具备了CPU的执行权,而BC处于临时阻塞状态。

         又,当CPU切换到B线程时,B就具备执行权,这时AC处于临时阻塞状态,只具备执行资格,不具备执行权。

 

         冻结状态:当前线程释放了CPU的执行权,并释放执行资格。冻结状态的结束,表示线程重新具备了执行资格。但是不一定立刻具备执行权。比如:睡眠时间到,或者被wait的线程唤醒。

 

>>线程的冻结:放弃了执行资格

         sleep(time);

        

         wait();//唤醒notify();

 

线程的消亡stop();//run方法结束

 

临时阻塞状态:具备运行资格但没有执行权,唤醒时先回到冻结状态

 

>>Thread的构造函数可以传线程名称

所以Thread的子类可以这样:

ZitTread(String name){super(name);}

 

>>Thread.currentThread()等于this

 

>>eg:简单的卖票程序,多个窗口卖票:创建线程的第二种方式:Runnable接口

1、定义类实现Runnable接口

2、覆盖Runnable接口中的run方法。

         将线程要运行的代码存放在run方法中。

3、通过Thread类建立线程对象。

4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

         为什么要将Runnable接口的子类对象传递给Thread的构造函数,因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。

5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

特点:

1、描述事物的类中封装了属性和行为,如果有部分代码需要被多线程所执行,同时还在操作属性。那么可以通过实现Runnable接口的方式。因为该方式是定义一个Runnable接口的子类对象,可以被多个线程所操作,实现了数据的共享。

2、实现了Runnable接口的好处,避免了单继承的局限性。也就是说,一个类如果已经有了自己的父类是不可以继承Thread类的,但是该类中还有需要被多线程执行的代码。这时就可以通过在该类上功能扩展的形式实现一个Runnable接口。

 

过程代码部分:

//class Ticket extends Thread

class Ticket implements Runnable

{

         //privatestatic int tick = 100;//方法1中定义静态可以防止重复卖票

         privateint tick = 100;//tick在堆内存中

         publicvoid run()

         {

                   while(true)

                   {

                            if(tick>0)

                            {

                                     System.out.println(currentThread().getName()+"sale:"+tick--);

                            }

                   }

         }

}

 

class TicketDemo

{

         publicstatic void main(String[] args)

         {

                   //方法4

                   Tickett = new Ticket();//tRunnable的实现,不是线程

                  

                   Threadt1 = new Thread(t);//创建了一个线程,注意里面的t参数

                   Threadt2 = new Thread(t);//创建了一个线程,注意里面的t参数

                   Threadt3 = new Thread(t);//创建了一个线程,注意里面的t参数

                   Threadt4 = new Thread(t);//创建了一个线程,注意里面的t参数

                  

                   t1.start();

                   t2.start();

                   t3.start();

                   t4.start();

                   /*方法3

                   Tickett = new Ticket();

                  

                   Threadt1 = new Thread();//创建了一个线程

                   Threadt2 = new Thread();//创建了一个线程

                   Threadt3 = new Thread();//创建了一个线程

                   Threadt4 = new Thread();//创建了一个线程

                  

                   t1.start();

                   t2.start();

                   t3.start();

                   t4.start();

                   运行的是Threadstart方法,所以什么都没有输出,怎么

                   才能让Ticket里面的run方法和线程发生关系呢?

                   解决方案:线程一开始就要明确所执行的代码,所以

                   Thread中有一个特殊的构造函数:Thread(runnabletarget)

                   看方法4

                   */

                  

                   /*方法2

                   如果票的张数tick不定义静态而这样写:

                   Tickett1 = new Ticket();

                  

                   t1.start();

                   t1.start();

                   t1.start();

                   t1.start();//同时跑4

                  

                   这样会报无效线程状态异常,start()代表已经从创建状态

                   到运行状态了,再次开启start()没有意义

                   解决方案:Runnable接口,其中就一个方法run();

                   */

                  

                  

                   /*方法1

                   Tickett1 = new Ticket();

                   Tickett2 = new Ticket();

                   Tickett3 = new Ticket();

                   Tickett4 = new Ticket();

                  

                   t1.start();

                   t2.start();

                   t3.start();

                   t4.start();*/

                  

                  

         }

}

 

>>那么,实现方式和继承方式有什么区别么?

实现方式好处:避免了单继承的局限性。

举个例子,如果Student类继承了Person类,但是Student内又有需要多线程运行的代码,这时Student不能再继承Thread类了,只有通过实现来解决。在定义线程时,建议使用实现方式。

 

两种方式区别:

继承Thread:线程代码存放Thread子类run方法中。

         实现Runnable,线程代码存在接口的子类的run方法。

 

 

 

>>eg     tick=1

if(tick>0)

                   {

                            system………………tick--);

                   }

情况:系统中,0线程开始运行,刚刚判断完tick>0后,还没输出,冻结;这个时候1线程进来了……判断完tick>0后,还没输出,冻结,换0线程运行……这个时候tick已经为1了,当0线程输出完后,x—tick0,。这个时候1线程执行,因为之前已经判断过,所以直接输出,输出0。这样就造成了错误!

tick=1

if(tick>0)

                   {       

                            try

{

         Thread.sleep(10);//10毫秒

}

catch(Exception e){}

                            system.out.println(Thread.currentThread().getName()+tick--);

                   }//可以观察到错误现象

 

         问题的原因:

         当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

 

解决办法:

         对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

 

         解决方案:同步代码块

synchronized(对象)

{

                   需要被同步的代码

}

 

egclass Ticketimplements Runnable

{

         private int tick = 1000;

         Object obj = new Object();

         public void run()

         {

                   while(true)

                   {

                            synchronized(obj)

                            {

                                     if(tick>0)

                                     {

                                               System.out.println(Thread.currentThread().getName()

+"....sale : "+ tick--);

                                     }

……

 

>>同步的前提

1、必须要有两个或者两个以上的线程

2、必须是多个线程使用同一个锁

 

必须保证同步中只有一个线程在运行。

同步能解决多线程的安全问题,但是同时如果有多个线程需要判断锁,则较为消耗资源。

 

>>如何找安全问题:

1,明确哪些代码是多线程运行代码。

2,明确共享数据。

3,明确多线程运行代码中哪些语句是操作共享数据的。

 

>>函数封装代码和函数封装代码有什么不同呢?就差别在同步上!

所以,用synchronized修饰函数便可,变成同步函数!

egpublic synchronizedvoid Demo(){}

 

>>函数需要被对象调用,那么函数都有一个所属对象引用。就是this

所以同步函数使用的锁是this

eg:注意用同步的两个前提来理解,不满足前提会不安全,出现0号票的情况

class Ticket implements Runnable

{

         privateint tick = 100;

         Objectobj = new Object();

         booleanflag = true;

         publicvoid run()

         {

                   if(flag)

                   {

                            while(true)

                            {

                                     synchronized(this)

                                     {

                                               if(tick>0)

                                               {

                                                        try{Thread.sleep(10);}catch(Exceptione){}

                                                        System.out.println(Thread.currentThread().getName()+

"....code : "+ tick--);

                                               }

                                     }

                            }

                   }

                   else//这个elseif(flag)

                            while(true)

                                     show();

         }

         publicsynchronized void show()//this

         {

                   if(tick>0)

                   {

                            try{Thread.sleep(10);}catch(Exceptione){}

                            System.out.println(Thread.currentThread().getName()+"....show....: "+ tick--);

                   }

         }

}

 

class ThisLockDemo

{

         publicstatic void main(String[] args)

         {

 

                   Tickett = new Ticket();

 

                   Threadt1 = new Thread(t);

                   Threadt2 = new Thread(t);

                   t1.start();                   //run方法

 

                   //但是主线程开启t1后,t1只有执行资格没有执行权

 

                   try{Thread.sleep(10);}catch(Exceptione){}               //所以要等待下

 

                   t.flag= false;             //否则系统直接执行完赋值false的代码,

 

//这时如果t1仍然只有执行资格的话,就只有show方法运行

 

                   t2.start();                   //show方法

         }

}

 

>>如果同步函数被静态修饰,锁就不再是this了,因为静态方法中也不可以定义this。静态方法进内存的时候是没有本类对象的,但是一定有该类对应的字节码文件对象:类名.class,该对象类型是Class

 

一个类加载进内存,会先将这个类对应的字节码文件封装成对象。该对象的表示方式“类名.class”代表一个字节码文件对象,该对象在内存中是唯一的。所以静态函数使用的锁为该类的字节码文件。

 

>>复习笔记2面向对象的懒汉式和饿汉式,注意懒汉式的同步锁是该类的字节码文件。

 

eg:饿汉式

………………

class Single

{

         privateSingle(){}

         privatestatic final Single s = new Single();

        

         publicstatic Single getInstance()

         {

                   returns;

         }

}

………………

 

eg:懒汉式

………………

class Single

{

         privateSingle(){}

         privatestatic Single s = null;

        

        

         publicstatic synchronized Single getInstance()

         {

                   //单例设计模式要保证对象的唯一性

                   //这里容易产生多线程操作问题,故而加上同步

                  

                   if(s== null)

                   {

                            s= new Single();

                   }

                   elsereturn s;

         }

}

………………但是这样要判断多次锁,没有效率

为了解决效率问题,采用双重判断模式

………………

class Single

{

         privateSingle(){}

         privatestatic Single s = null;

public staticSingle getInstance()

         {

                   if(s== null)

                   {

                            synchronized(Single.class)//注意,静态,锁不是this

                            {

                                     if(s== null)//注意,这边的判断也是必要的

                                     {

                                               s= new Single();

                                     }

                            }

                   }

                   returns;

         }

}

 

>>虽然同步的出现解决了线程安全问题。但是也带来了一些弊端:

         1、效率会降低

         2、容易造成死锁

 

>>同步中嵌套同步,锁却不同,会造成死锁。

egclass Testimplements Runnable

{

          private boolean flag;

          Test(boolean flag)

          {

                          this.flag= flag;

          }

          

          public void run()

          {

                          if(flag== true)

                          {

                                    synchronized(MyLock.lockA)

                                    {

                                             System.out.println(Thread.currentThread().getName()+"--if---A");

                                             synchronized(MyLock.lockB)

                                             {

                                                      System.out.println(Thread.currentThread().getName()+"--if---B");

                                             }

                                    }

                          }

                          else

                          {

                                    synchronized(MyLock.lockB)

                                    {

                                             System.out.println(Thread.currentThread().getName()+"--else---B");

                                             synchronized(MyLock.lockA)

                                             {

                                                      System.out.println(Thread.currentThread().getName()+"--else---A");

                                             }

                                    }

                          }

          }

}

 

class MyLock

{

         publicstatic Object lockA = new Object();

         publicstatic Object lockB = new Object();

}

 

class DeadLockTest

{

         publicstatic void main(String[] args)

         {

                   Testt1 = new Test(true);

                   Testt2 = new Test(false);

                  

                   Threadth1 = new Thread(t1,"小强");

                   Threadth2 = new Thread(t2,"旺财");

                  

                   th1.start();

                   th2.start();

         }

}

输出结果:(输出结果会变化)

f:\java\workspace>java DeadLockTest

旺财--else---B

小强--if---A

 

f:\java\workspace>java DeadLockTest

小强--if---A

旺财--else---B

 

所以,尽量避免同步嵌套。

 

自己的解决方案,加上了while循环

eg:………………

public void run()

          {

                         

                           if(flag== true)

                            {

                                    while(true)

                                    {

                                              synchronized(MyLock.lockA)

                                              {

                                                       System.out.println(Thread.currentThread().getName()+"--if---A");

                                                       synchronized(MyLock.lockA)

                                                       {

                                                       System.out.println(Thread.currentThread().getName()+"--if---A");

                                                       }

                                              }

                                    }

                           }

                          else

                          {

                                    while(true)

                                    {

                                              synchronized(MyLock.lockB)

                                              {

                                                       System.out.println(Thread.currentThread().getName()+"--else---B");

                                                       synchronized(MyLock.lockB)

{                                                                         System.out.println(Thread.currentThread().getName()+"--else---B");

                                                       }

                                              }

                                    }

                          }

          }

………………

 

>>eg:线程间通信,其实就是多个线程在操作同一个资源(Resource),但是操作的动作不同。

         比如一辆车往仓库里送货,而另一辆车往外拉货。

class Res

{

         Stringname;

         Stringsex;

}

 

class Input implements Runnable

{

         //Objectobj = new Object();//用的不是同一个锁,也会出现问题

private Res r ;//Res是内存中的唯一的,可以作为锁

         Input(Resr)

         {

                   this.r= r;

         }

         publicvoid run()

         {

                   intx = 0;

                   while(true)

                   {

                            synchronized(r)//注意,两个方法处理同一个资源,两个都要同步

                            {

                                     if(x==0)

                                     {

                                               r.name="mike";

                                               r.sex="man";

                                     }

                                     else

                                     {

                                               r.name="丽丽";

                                               r.sex= "女女女女女";

                                     }

                                     x= (x+1)%2;

                            }

                   }

         }

}

 

class Output implements Runnable

{

private Res r ; //Res是内存中的唯一的,可以作为锁

         //Objectobj = new Object();//用的不是同一个锁,也会出现问题

         Output(Resr)

         {

                   this.r= r;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            synchronized(r)//注意,两个方法处理同一个资源,两个都要同步,

//这里不能用obj

                            {

                                     System.out.println(r.name+"...."+r.sex);

                            }

                   }

         }

}

 

class InputOutputDemo

{

         publicstatic void main(String[] args)

         {

                   Resr = new Res();//Res是唯一的,可以作为锁

 

                   Inputin = new Input(r);

                   Outputout = new Output(r);

 

                   Threadt1 = new Thread(in);

                   Threadt2 = new Thread(out);

 

                   t1.start();

                   t2.start();

         }

}

 

>>等待/唤醒机制

wait():让线程等待,将线程存储到一个线程池中。

notify():唤醒被等待的线程,通常都唤醒线程池中的第一个,让被唤醒的线程处于临时阻塞状态。

notifyAll():唤醒所有的等待线程,将线程池中所有线程都唤醒,让他们从冻结状态转到临时阻塞状态。

 

注意,等待唤醒机制,通常都用在同步中,因为需要锁的支持。而且必须要明确wait notify所作用的锁对象。

 

         这三个方法用于操作线程,可是定义在了Object类中,why?

因为,这三个方法在使用时,都需要定义在同步中,要明确这些方法锁操作的线程所属于的锁。简单说,在A锁被wait的线程,只能被A锁的notify唤醒。

 

>>线程运行的时候,内存中建立线程池,唤醒时,从线程池唤醒,通常唤醒第一个等待的线程。wait();notify();notifyAll();是属于Object类的,他们都是用在同步当中的。

eg:输入一个信息,就打印一个信息

class Resource

{

         Stringname;

         Stringsex;

         booleanb;

}

 

class Input implements Runnable

{

         privateResource res;

         privateint count = 0;

        

         Input(Resourcer)

         {

                   this.res= r;

         }

        

         publicvoid run()

         {

                   intx = 0;

                   while(true)

                   {

                            synchronized(res)

                            {

                                     if(res.b)

                                     {

                                               //wait()会抛出异常

                                               try{res.wait();}catch(Exceptione){}//必须标示wait所属的锁r.wait();

                                     }

                                    

                                     if(x== 0)

                                     {

                                               res.name= "小青";

                                               res.sex= ""+count++;

                                     }

                                     else

                                     {

                                               res.name= "志明";

                                               res.sex= ""+count++;

                                     }

                                     res.b= true;

                                     res.notify();

//必须标示notify所属的锁r.notify();不可以对不同锁中的线程进行唤醒

                            }

                            x= (x+1)%2;

                   }

         }

}

 

class Output implements Runnable

{

         //privateint count = 0;

         privateResource res;

        

         Output(Resourcer)

         {

                   this.res= r;

         }

        

         publicvoid run()

         {

                   while(true)

                   {       

                            synchronized(res)

                            {

                                     if(!res.b)

                                     {

                                               try{res.wait();}catch(Exceptione){}

                                     }

                                      System.out.println(res.name+":"+res.sex);

                                     res.b= false;

                                     res.notify();

                            }

                   }

         }

}

 

class ResourceDengDaiHuanXing

{

         publicstatic void main(String[] args)

         {

                   Resourceresource = new Resource();

                   Inputin = new Input(resource);

                   Outputout = new Output(resource);

                  

                   Threadinn = new Thread(in);

                   Threadoutt = new Thread(out);

                  

                   inn.start();

                   outt.start();

         }

}

 

//优化上述代码

class Resource

{

         Stringname;

         Stringsex;

         booleanb;

        

         //封装Input中的代码

         publicsynchronized void set(String name,String sex)

         {

                   if(b)

                   {

                            try{this.wait();}catch(InterruptedExceptione){}

                   }

                   //为了保证namesex一次性被赋值,所以set方法定义为同步函数

                   this.name= name;

                   this.sex= sex;

                  

                   b= true;

                   this.notify();

         }

        

         //封装Output中的代码

         publicsynchronized void get()

         {

                   if(!b)

                   {

                            try{this.wait();}catch(InterruptedExceptione){}

                   }

                   为了保证namesex一次性被输出,所以set方法定义为同步函数

                   System.out.println(this.name+":"+this.sex);

                  

                   this.b= false;

                   this.notify();

         }

}

 

class Input implements Runnable

{

         privateResource res;

         privateint count = 0;

        

         Input(Resourcer)

         {

                   this.res= r;

         }

        

         publicvoid run()

         {

                   intx = 0;

                   while(true)

                   {

                            if(x== 0)

                            {       

                                               res.set("小青","");

                            }                

                            else

                            {

                                               res.set("志明","");

                            }       

                            x= (x+1)%2;

                   }

         }

        

}

 

class Output implements Runnable

{

         //privateint count = 0;

         privateResource res;

        

         Output(Resourcer)

         {

                   this.res= r;

         }

        

         publicvoid run()

         {

                   while(true)

                   {       

                            res.get();

                   }

         }

}

 

 

//进行优化

class ResourceDengDaiHuanXing

{

         publicstatic void main(String[] args)

         {

                   Resourceresource = new Resource();

                   Inputin = new Input(resource);

                   Outputout = new Output(resource);

                  

                   Threadinn = new Thread(in);

                   Threadoutt = new Thread(out);

                  

                   inn.start();

                   outt.start();

         }

}

 

>>eg:多线程生产和多线程消费

情况,生产多件,消费一件

class ProducerConsumerDemo

{

         publicstatic void main(String[] args)

         {

                   Resourcer = new Resource();

                  

                   Producerpro = new Producer(r);

                   Consumercon = new Consumer(r);

                  

                   Threadt1 = new Thread(pro);

                   Threadt2 = new Thread(con);

                  

                   /*如果这样来实现多生产者和多消费者

                     Thread t1 = new Thread(pro);

                     Thread t2 = new Thread(pro);

                            Threadt3 = new Thread(con);

                            Threadt4 = new Thread(con);

                            通过whilenotifyAll()解决

                   */

                  

                   t1.start();

                   t2.start();

                  

                   /*会出现生产多个消费一个和生产一个多个消费的错误

                            t1.start();

                            t2.start();

                            t3.start();

                            t4.start();

                            通过whilenotifyAll()解决

                   */

         }

}

 

class Resource

{

         privateString name;

         privateint count = 1;

         privateboolean flag = false;

         //t1t2

         publicsynchronized void set(String name)

         {

                   //if(flag)

                   while(flag)

                  /*根据NO.6,处理:既然没有判断,改成while怎样?

                   结果全部等待,更改notify();解决*/

                   {       

                            try{wait();}catch(Exceptione){}

                            /*---NO.1---

                            第一次执行后t1在这里放弃资格,假设t2下一个获取执行权,

                            但是也在这里放弃资格,剩下t3t4

                            */

                           

                            /*---NO.6---

                            (关键)t2原本在这里放弃资格,醒来后,不做判断flag

                            向下执行代码“this.name= name+"--"+count++;

                            */

                   }

                                    

                   this.name= name+"--"+count++;//姓名号数一起赋值,号数自加

                   /*---NO.7---

                   t2生产了一个,覆盖了t1的生产

                   */

                  

                   System.out.println(Thread.currentThread().getName()+

                            "--生产者--"+this.name);//---NO.4---t1生产了一个

                                                       

                   flag= true;

                   //this.notify();//---NO.5---(关键)t1现在把t2唤醒

                                                                                    //---NO.8---唤醒t3,消费了t2生产的号码

                   this.notifyAll();//解决全部等待问题

         }

        

         //t3t4

         publicsynchronized void out()

         {

                   //if(!flag)

                   while(!flag)

                   /*根据NO.6,处理:既然没有判断,改成while怎样?结果全部等待*/

                   {

                            try{wait();}catch(Exceptione){}

                            /*---NO.3---

                            t1被唤醒但是不一定有执行权,t3到这里放弃资格,

                            这时醒着的有t1t4t4到这里放弃资格

                            */

                   }

                           

                   System.out.println(Thread.currentThread().getName()+

                            "--消费者--"+this.name);

                                                       

                   flag= false;

                   //this.notify();//---NO.2---假设t3执行到此,唤醒t1

                   this.notifyAll();//解决全部等待问题

         }

}

 

class Producer implements Runnable

{

         privateResource res;

         Producer(Resourceres)

         {

                   this.res= res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            res.set("+商品+");

                   }

         }

}

 

class Consumer implements Runnable

{

         privateResource res;

         Consumer(Resourceres)

         {

                   this.res= res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            res.out();

                   }

         }

}

 

同理,生产一个消费多个的情况,是因为t1-t4线程在争夺cpu资源时,获取权限的顺序不同造成的。

 

>>JDK5.01.5)升级版提供了java.util.concurrent.locks包,里面有lock接口,其中有lock()unlock()方法-显示的加减锁方式。

 

synchronized使用的是隐式的锁操作。

Lock接口,使用的是显式的。

 

另外还有Condition接口,该对象的出现替代了Object中的waitnotifynotifyAll

其中有await()让线程等待,signal()唤醒一个线程和signalAll()唤醒全部线程的方法。

 

eg: import java.util.concurrent.locks.*;//由于是新特性,注意导入包

 

class ProducerConsumerDemo2

{

         publicstatic void main(String[] args)

         {

                   Resourcer = new Resource();

 

                   Producerpro = new Producer(r);

                   Consumercon = new Consumer(r);

 

                   Threadt1 = new Thread(pro);

                   Threadt2 = new Thread(pro);

                   Threadt3 = new Thread(con);

                   Threadt4 = new Thread(con);

 

                   t1.start();

                   t2.start();

                   t3.start();

                   t4.start();

 

         }

}

 

class Resource

{

         privateString name;

         privateint count = 1;

         privateboolean flag = false;

        

         //定义一个锁,ReentrantLockLock子类

         privateLock lock = new ReentrantLock();

 

         /*API中的定义

         一个可重入的互斥锁Lock,它具有与使用 synchronized

         方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,

         但功能更强大。

         */

        

/*

通过指定的锁,创建了该锁上可以使用的监视器对象

且一个lock可以搭载多个Condition

*/

         privateCondition condition_pro = lock.newCondition();

         privateCondition condition_con = lock.newCondition();

 

        

         /*API中得定义

         Condition Object监视器方法(waitnotify notifyAll

         分解成截然不同的对象,以便通过将这些对象与任意 Lock实现组合使用,

         为每个对象提供多个等待setwait-set)。

         */

 

 

         public  void set(String name)throwsInterruptedException

         {

                   lock.lock();//获取锁,相当于wait();

                   try

                   {

                            while(flag)

                                     condition_pro.await();//await()会抛出异常

                            this.name= name+"--"+count++;

 

                            System.out.println(Thread.currentThread().getName()

                                     +"...生产者.."+this.name);

                            flag= true;

                            condition_con.signal();//不用condition.signalALL()

/*注意,唤醒不要弄错了,这里如果为condition_pro.signal();会死锁*/

                   }

                   finally//注意

                   {

                            lock.unlock();//释放锁的动作一定要执行。

                   }

         }

 

 

         public  void out()throws InterruptedException

         {

                   lock.lock();

                   try

                   {

                            while(!flag)

                                     condition_con.await();

                            System.out.println(Thread.currentThread().getName()

                                     +"...消费者........."+this.name);

                            flag= false;

                            condition_pro.signal();

/*注意,唤醒不要弄错了,这里如果为condition_con.signal();会死锁*/

                   }

                   finally

                   {

                            lock.unlock();

                   }

                  

         }

}

 

class Producer implements Runnable

{

         privateResource res;

 

         Producer(Resourceres)

         {

                   this.res= res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            try

                            {

                                     res.set("+商品+");//注意set方法抛出异常

                            }

                            catch(InterruptedException e)

                            {

                            }

                           

                   }

         }

}

 

class Consumer implements Runnable

{

         privateResource res;

 

         Consumer(Resourceres)

         {

                   this.res= res;

         }

         publicvoid run()

         {

                   while(true)

                   {

                            try

                            {

                                     res.out();//注意out方法抛出异常

                            }

                            catch(InterruptedException e)

                            {

                            }

                   }

         }

}

 

其中特别要注意的一点,就像使用notify而不是notifyAll一样,如果在Resource类的set方法中写成condition_pro.signal();而在out方法中写成condition_con.signal();,就会造成死锁!全体线程等待!所以,应该是负责生产的线程生产了一件产品,然后唤醒(condition_con.signal();)输出线程消费,消费完了,再唤醒(condition_pro.signal();)负责生产的线程生产产品!两个线程执行完了以后要唤醒对方!

 

>>一些方法细节

sleepwait有什么区别?

对时间的指定

1sleep方法必须指定时间

2wait方法有重载形式,可以指定时间,也可以不指定时间。

 

对于执行权和锁的操作

1sleep():释放执行权,不释放锁,因为肯定能醒,肯定可以恢复临时阻塞状态。

2wait():释放执行权,释放锁,因为wait不释放锁,如果没有时间指定,那么其他线程都进不了同步中,无法将其唤醒。

 

注意,同步中可以有多个存活的线程,但是只能有一个去执行同步的代码。因为只有一个线程会持有同步的锁,只有当该线程释放了锁,其他线程才会有机会获取到锁,而且只能有一个线程获取到锁,继续执行。

 

>>让线程停止,有stop()方法,但是stop()方法已经过时(固有的不安全性,强制停止任何线程)。

如何停止线程?只有一种原理,run()方法结束。另外多线程运行代码通常是循环结构,只要控制住循环,就可以让run方法结束。

但是如果使用标记(flag=true orfalse)会有弊端。

特殊情况:当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。这个时候可以调用interrupt()方法,将处于冻结状态的线程,强制回到运行状态上来,它会返回一个InterruptedException异常。注意,中断(冻结)状态不是停止线程,stop才是停止。

 

>>setDaemon()将该线程标记为守护线程(后台线程)。该方法必须在启动线程前调用。

 

线程分前台和后台两种。运行方式都一样都会获取cpu的执行权执行。不同的在于,结束方式不同。前台线程只有run方法结束,才结束。后台线程,run方法结束,结束,还有,如果run方法没结束,而前台线程都结束了。后台线程一样自动结束。

 

所以一个进程是否结束参考的是,是否还有前台线程存活。如果前台线程都结束了,那么进程也就是结束了。

 

eg:…………

t1.setDaemon(true);

t1.start();

…………

“前台线程”全部结束,守护线程自动结束。

 

eginterrupt方法与setDaemon方法例子

class StopThread implements Runnable

{

         privateboolean flag = true;

         publicsynchronized void run()

         {

                   while(flag)

                   {

                            try

                            {

                                     wait();//t1t2

                            }

                            catch(InterruptedException e)

                            {

                                     System.out.println(Thread.currentThread().getName()+".....Exception");

                                     setFlag();

                            }

                            System.out.println(Thread.currentThread().getName()+".....");

                   }

         }

         publicvoid setFlag()

         {

                   flag= false;

         }

}

 

class StopThreadDemo

{

         publicstatic void main(String[] args)

         {

                   StopThreadst = new StopThread();

 

                   Threadt1 = new Thread(st);

                   Threadt2 = new Thread(st);

                  

//将两个线程全标记成后台线程。

                   t1.setDaemon(true);

                   t2.setDaemon(true);

 

                   t1.start();

                   t2.start();

 

                   intnum = 1;

                   while(true)

                   {

                            if(num++==50)

                            {

                                     //st.setFlag();

                                     //t1.interrupt();//t1的线程冻结状态清除,强制让其恢复到运行状态来。

                                     //但是这种强制动作会发生异常,需要对个中断异常进行处理.

                                     //只有恢复到运行状态中来才有机会执行标记。

                                     //t2.interrupt();

                                     break;

                            }

                           

                            System.out.println(Thread.currentThread().getName()+"....."+num);

                   }

         }

}

 

>>egjoin方法

…………main

…………

t1.start();

try

{

t1.join();//表示t1需要cpu执行权,这时主线程冻结直到t1结束。

}

catch(InterruptedException e){}

t2.start();

…………

//如果这样

…………main

…………

t1.start();

t2.start();

t1.join();//t1t2交替执行,但是仍然是主线程等到t1执行完后开始执行,与t2无关

…………

 

会抛出:InterruptedException-如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。

 

join方法可以用来临时加入线程执行。

 

>>线程中其它方法:

toString():返回线程名称、优先级、所属组等信息,其覆盖Object的方法。

egSystem.out.println(Thread.currentThread().toString());

 

一般情况下,谁开启的线程,线程就属于谁,比如一般属于main组,可以用ThreadGroup类更改。

 

优先级(包括主线程)默认是5,使用setPriorityintnewPriority);方法更改,一共10级。其中1510,对应的常量Thread.MIN_PRIORITYThread.NORM_PRIORITYThread.MAX_PRIORITY,一般使用常量以提高阅读性。

eg:………………

t1.start();

t1.setPriority(Thread.MAX_PRIORITY);

t2.start();

…………

 

yield():暂停当前正在执行的线程对象,执行其它线程。

eg: class Demo implements Runnable

{

         publicvoid run()

         {

                   for(intx=0; x<60; x++)

                   {

                            System.out.println(Thread.currentThread().toString()+"....."+x);

                            Thread.yield();

//临时暂停。可以达到一种较为和谐的状态。对线程的运行的进行暂缓。

                                    

                   }

         }

}

………………

 

>>两种建立线程的技巧方法:

/*one*/

Runnable r = new Runnable()

                   {

                            publicvoid run()

                            {

                                     for(intx=0; x<100; x++)

                                     {

                                               System.out.println(Thread.currentThread().getName()+"....."+x);

                                     }

                            }

                   };//不能调用start方法

new Thread(r).start();

/*two*/

public static void main(String[] args)

         {

                   newThread()

                   {

                            publicvoid run()

                            {

                                     for(intx=0; x<100; x++)

                                     {

                                               System.out.println(Thread.currentThread().getName()+"....."+x);

                                     }

                            }

                   }.start();//注意调用start方法

                             

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值