多线程编程

来源:互联网 发布:swift 商城源码 编辑:程序博客网 时间:2024/03/29 09:49

A基本概念   

 首先必须强调程序,进程,线程三个概念的联系和区别。程序,是存储于磁盘之中的代码序列,进程是一个程序在自身地址空间中的一次执行,一个程序可以有多个进程,比如我们可以同时打开多个windows的记事本,这就是记事本程序的多个进程。线程是进程中的一个单一的连续控制流,一个进程允许有多个线程。线程之间的信息交换将快于进程,这是必须记住的。

B。线程类 

   在JAVA的虚拟机中(JVM),要实现一个程序的多线程有两种方法,一是实现Runnable接口,该接口中唯一函数是run();二是继承自Thread类。这两个方法的实质是实现Runnable接口,因为Thread类就是实现了Runnable接口。另,Thread类在java.lang包下。
 我们来分开讲述:
   一。继承自Thread类,常用的函数如下:(非静态函数未注明)
    1. currentThread()静态函数,可以获得当前运行的线程对象
    2. getName(),获得线程的名字;
    3.run(),线程运行时会运行该函数中的内容,一般要重写
    4.setDaemon(boolean pa),pa为真时将线程设置为后台运行,必须在start()执行之前设置;
    5.start(),线程对象开始运行
    6.yield(),线程放弃运行,让其他程序运行
    7.setPriority(int i),设置线程优先级,级数为1-10,默认优先级为5
    8.sleep(long l)让线程暂停指定毫秒
    9.interrupt(),中止线程。
    此外,有 wait()和notify(),是来自java.lang.object类中,下文会另有篇幅详细叙述。
    JVM支持不同优先级的线程抢占,但是它实质上是不支持相同优先级的时间片轮转的,要实现相同优先级的线程的时间片轮转,必须依赖于JVM当前的操作系统平台。如果当前的操作系统支持相同优先级线程的时间片轮转,则JVM可以支持相同优先级线程的时间片轮转,否则,不能支持。
  二。实现Runnable接口,该接口有且只有一个方法run();事实上,Thread类实现了这个接口。
 这里遇到了一个麻烦,因为JAVA不支持类的多继承,如果某一个线程类已经存在基类(super),那么他将无法再继承Thread类,但是有一个解决的方法,即依赖于内部类的继承来实现,这很显然会给我们对代码的控制造成影响,因此采用实现Runnable接口方式会更加的方便。但是这也要依照情况而论,毕竟Thread类中提供了很多有用的方法。
   
C.线程的同步
 若多个线程要与某个对象关联,如果控制呢?我们设想以下情况,有线程a和b,要关联对象x,由于a和b是各自独立的,因此他们对x的修改都会对另一个造成影响,这势必造成混乱。我们必须限制当a访问x时候,b不能访问x,而当b访问x的时候,a同样不能访问x,即在某一个时刻,只能允许一个线程来访问x。
 在java中提供了synchronized语法。有两种使用方式
  一。Synchronized(任意对象)
                {
                      语句
                }
    即同步块,可以和任意对象关联
  二。 将某个方法声明为synchronized,即同步方法,这个方法与this指向的对象关联。因此,要实现同步块和同步方法之间也同步,必须让同步块关联到this,synchronized(this)。
 在JVM中,任意对象都有一个等待队列和一个监视器(monitor),同一时刻只能有一个线程将这个监视器"锁住",其他线程试图进入该对象,则必须等待,进入等待队列,直到监视器被“松开”。这里要用到Object类中的wait()和notify()方法 。 wait()可以让某个线程对象进入等待队列,而notify()方法则从等待队列中取出某一个线程,该线程也不能马上运行,而必须等到监视器被“松开”。另还有notifyAll(),可以取出等待队列中的所有线程。注意,wait()和notify()必须是对在一个同步块中的对象使用。
 
D。线程的死锁。
 设想以下情况,线程a和b,对象X和Y,a和b都要关联对象XY,在某个时刻,a已经锁住X的监视器,此时由于某种情况(如sleep()或者wait()),它被暂停而b得到运行,b会锁住Y的监视器,并等待a的监视器被“松开”。之后,a得以继续运行。这样陷入了尴尬的局面,a在等待Y的监视器,而b则在等待X的监视器,他们两各不相让,相持直到太阳变成超星星并爆炸毁灭地球都不会停止。这就是所谓死锁,在多线程编程时一定要避免这种情况。
   
以下是我写的一个生产者_消费者(Producer_Comsumer)模式的程序,期间出现了一个问题,即程序的打印结果出来以后,程序的运行还是没有停止。后来我发现,当main()退出以后,消费者线程仍然处于Queue的等待队列中,因此程序没有停止,我不得不在61行使用了Thread.currentThread().interrupt();来强制终止它。我相信随着我的练习,我会写出更加优美的语句。该程序在jdk1.5下运行通过,并可以完美退出。
class PCTest
{
 public static void main(String[] args)
 {
   int i=0;
   i++;
   Queue que=new Queue();
   Producer pro=new Producer(que);
   Comsumer com=new Comsumer(que);
   try
   {
     pro.start();
    }
    catch(IllegalThreadStateException e)
    {
     e.printStackTrace();
    }
   try
   {  
     com.start();
   }
   catch(IllegalThreadStateException  e)
   {
    e.printStackTrace();
   }
 }
}
class Queue
{
 int i=0;
 int num;
 boolean flag=false;
  synchronized void putNum(int num)
 {
  if(flag==false)
  {
   this.num=num;
   System.out.println("The Producer has produced  "+num);
   this.flag=true;
   notify();
   try
   {
     wait();
   }
   catch(Exception e)
   {
    e.printStackTrace();
   }
  }
 }
 synchronized void getNum()
 {
  i++;
  if(flag==true)
  {
   System.out.println("The Comsumer has comsumed  "+num);
    flag=false;
    notify();
    if(i==10)
    {
     Thread.currentThread().interrupt();
    }
    else
    {
      try
      {
       wait();
      }
      catch(Exception e)
      {
       e.printStackTrace();
      }
    }
  }
 }
}
class Producer extends Thread
{
 Queue que;
 Producer(Queue que)
 {
  this.que=que;
 }
 public void run()
 {
   for(int i=0;i<10;i++)
   {  
     que.putNum(i);
   }
 }
}
class Comsumer extends Thread
{
 Queue que;
 Comsumer(Queue que)
 {
  this.que=que;
 }
 public void run()
 {
  for(int i=0;i<10;i++)
  {
   que.getNum();
  }
 }

原创粉丝点击