java-多线程编程<三>

1.进程

        每个独立进行的程序称为进程,即“正在进行的程序”,进程都有自己独立的内存空间,

        如果某个进程去访问其他进程的内存空间,则有可能是病毒来的,操作系统的多任务

        其实是cpu以非常小的时间间隔交替执行多个程序,给人同时进行多个程序的感觉。

2.线程

       1.线程是轻量级的进程。

       2.线程没有独立的内存空间。

       3.线程是由进程产生,寄生于进程。

       4.一个进程可以有多个线程(就是我们所说的多线程编程)

3.线程的状态

      1.新建状态(new):新创建了一个线程对象。

      2.就绪状态(Runnable):对象创建后,其他线程调用该对象的start( )方法。

         该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

      3.运行状态(Running):就绪状态的线程获取CPU,执行程序代码

      4.阻塞状态(Blocked):线程因为某种原因放弃了CPU的使用权,暂时停止运行。

                                      直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况三种。

          a.等待阻塞:运行的线程执行wait( )方法,JVM把该线程放入等待池中

          b.同步阻塞:运行的线程在获取对象的同步锁时,如该同步锁被其他线程占用,则JVM把该线程放入锁池中

          c.其他阻塞: 运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。

                      当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

      5.死亡状态(Dead):线程执行完了,或因异常退出run()方法。结束生命周期。

4.线程使用

      a.继承Thread 类, 重写run函数,一个线程只能启动一个线程,无论调用多少次start方法,结果只有一个线程。

public class java_thread extends Thread{ public static void main(String args[]) {

      (new java_thread()).run();

      System.out.println("main thread run ");

}

      public synchronized void run()

     { System.out.println("sub thread run "); }

}

     b.实现Runnable接口,重写run函数

public class java_thread implements Runnable{ public static void main(String args[]) {

      (new Thread(new java_thread())).start();

      System.out.println("main thread run ");

}

    public void run()

   { System.out.println("sub thread run "); }

}

       c.直接在函数体使用

void java_thread()  
{  
  
     Thread t = new Thread(new Runnable(){  
            public void run(){  
            mSoundPoolMap.put(index, mSoundPool.load(filePath, index));  
            getThis().LoadMediaComplete();  
            }});  
        t.start();  
}

4.1   (Thread,Runnable的代理模式

      实现Runnable 接口优势:

           a.解决Java单一继承的限制

           b.适合多个代码相同的程序出处理同一资源

           c.增强程序的健壮性,代码可以被多个线程共享,代码与数据独立。

        继承Thread类优势:

           a.多线程同步。

           b.可以将线程类抽象出来,当需要使用抽象工厂模式设计时。

        在函数体使用优势:

           a.无需继承Thread或实现Runnable,缩小作用域。

5.后台线程与前台进程

    前台线程是为完成一项任务而存在的,后台线程却是服务性的,
    当一个一个程序的所有用户线程(前台线程和主线程)都已经停止运行的时候(因为错误、异常还是正常结束),

     jvm都将退出,作为后台服务而存在的后台线程也将退出,尽管这期间将有一点时间的延迟。

    一般后台线程用于处理时间较短的任务,如在一个Web服务器中可以利用后台线程来处理客户端发过来的请求信息

   而前台线程一般用于处理需要长时间等待的任务,如在Web服务器中的监听客户端请求的程序,或是定时对某些系

   统资源进行扫描的程序。

   两者关系:

   前台线程与后台线程,在调用start 方法前调用setDaemon (true)方法则该线程变成后台线程,

   如果不调用setDaemon (true)方法,则默认是前台线程,在java程序中,只要有一个前台线程

   还没结束,则这个java程序进程不会结束,直到最后一个前台线程结束,Java程序进程才结束,

   但是当java程序进程中,没有任何前台线程,只有后台线程,则该进程立即结束。

6.联合线程与join方法

   某线程调用join方法后,它将合并到当前其调用join方法所在线程中。

   由原本的两个线程合并为“一个线程”,按顺序的执行下去。

   main方法中:

 

 1 Threadtest  tt =new Threadtest();
 2            Thread t=new Thread (tt);
 3            t.start();
 4            while (true)
 5            {
 6                if (i==100)
 7                {
 8                try {
 9 t.join();
10 } catch (InterruptedException e) {
11 // TODO Auto-generated catch block
12 e.printStackTrace();
13 }
14                }
15                System.out.println("main thread runing"+i++);
16            }

7.线程同步:

  使用同步代码块:synchronized (Object) { 同步代码 }

  标志位(锁旗标): 任意类型的对象(监视器)都有一个标志位,标志位为01,初始默认是1。

   但执行synchronized (Object) 语句后,该标志位为0,直到执行完同步代码后,该对象的标志位才变回1

  其他线程在执行synchronized (Object) 之前会检查对象的标志位,当发现为0时,代表有线程正在执行

  同步代码,这时它们会进入到一个等待线程池中,处于阻塞状态,CPU不是人为控制的,CPU其实会

  切换到其他线程,但是发现它们处于阻塞状态,只好回到原来running的线程继续执行。

  同步的缺点:系统要不停的对同步监视器检查,造成了额外的开销,导致了为了安全性而做出了运行速度的牺牲。

8.同步函数:

      与同步代码块作用一样,在函数名前加synchronized 修饰。在一个类中如果有多个synchronized 修饰的方法,

    则可以再多个线程中实现同步。当某个线程执行某对象中的某个synchronized 修饰的方法时,其他的线程无法

    执行这对象中所有synchronized 修饰的方法,直到该线程执行完同步的方法,其他线程才可以去执行。

     注意:同步函数使用的监视器对象是this,即本身。

9.代码块与函数之间的同步

    只要它们的监视器是同一个对象即可以实现了,因为同步函数使用的监视器是this,所以代码块与函数的监视器应该是this才可以。

 

 1 public class testsychronized {
 2 public static void main(String[] args) {
     //启动一个线程tt
 3   testThread  tt=new testThread();
 4   new Thread(tt).start();
      //主线程main睡眠1000毫秒
 5  try {
 6         Thread.sleep(1000);
 7       } 
     catch (InterruptedException e) {
 8 // TODO Auto-generated catch block
 9 e.printStackTrace();
10 }
     //改变tt的标志位为1,交给另一个进程启动
11  tt.flag=1;
12  new Thread(tt).start();
13 }}

14 class testThread implements Runnable
15 {
         //默认标志位为0,100张票
16     int flag=0; 
17     private int ticket=100;
18    public void run() {
19        if (flag==0)
20         {
21             while(true)
22             {
23            try {
24                    Thread.sleep(100);
25                  } catch (InterruptedException e) {
26     // TODO Auto-generated catch block
27                     e.printStackTrace();
28                   }
                  //调用同步代码块,显示票数并减一
29                 synchronized(this)
30                   {
31                       if (ticket>0)
32                       System.out.println("block ( )"+ticket--);
33                    }}}
34         else 
               //标志位为1时,调用同步方法show
35             {
36              while(true)
37                 { 
38                  try {
39                        Thread.sleep(1000);
40                        } catch (InterruptedException e) {
41                 // TODO Auto-generated catch block
42                        e.printStackTrace();
43                           }
44              show();
45 }    }}
          //同步方法show,显示剩余票数,并减一
46      public synchronized void show ()
47    {
48        if (ticket>0)
49         System.out.println("show ( )"+ticket--);
50      }
51  }                                                

 

 

 

10.死锁问题

        当一个线程进入了对象x的监视器,而另一个线程已经进入了对象y的监视器,

     如果进入x的线程还想进入到对象y的监视,就会被阻隔,接着进入y的线程想进入对象x的监视器的话,这就造成死锁问题。

11.线程间的通信

      如果有两个线程,一个线程是对数据进行修改,而另一个线程是对线程进行读取。达到同步的情况,

      应该是添加一个数据后,立即读取该数据,以这样的方式不停的执行下去。

      当两线程之间不同步时,则可能发生以下情况

    a.只修改数据的部分内容,这时切换到另一个线程读取它的数据,将会是一部分是原来的数据,另一部分是已修改的数据。

     b.已经进行数据多次修改,另一个线程才开始读取,那只能读到最后一次的数据。也可能对数据重复读取多次后,才进行数据的修改

 

 1  //生产者线程
      class Producer implements Runnable
 2 {
 3    Q q=null;
       //初始化它的生产对象
 4    public Producer (Q q)
 5    {
 6    this.q=q ;
 7    }
 8   public void run() {
 9      while(true)
10     {
11       try {
12            Thread.sleep(100);
13            } catch (InterruptedException e) {
14         // TODO Auto-generated catch block
15       e.printStackTrace();
16      }
        //如果标志位为0,则添加对象
17    if(q.flag==0)
18    q.addSome();
19  }}}
      //消费者线程
20 class Comsuer implements Runnable
21 {
22    Q q=null ;
        //初始化它的消费对象
23    public Comsuer (Q q)
24    {
25    this.q=q ;
26    }
27    public void run() {
28      while(true)
29      {
30          try {
31          Thread.sleep(100);
32       } catch (InterruptedException e) {
33       // TODO Auto-generated catch block
34       e.printStackTrace();
35 }
       //标志位为1时,读取消费对象
36   if(q.flag==1)
37   q.readSome();
38   }}}
39 //将对数据Q的同步操作方法写入到Q中,因为同步的对象时Q,
40 //那直接将同步的方法写入Q中,再用生产者和消费者调用它的方法即可
41 class Q 
42 {
43     String name ;
44     String sex ;
       //默认标志位为0
45     int flag =0;
46     int i=0;
      //同步添加方法,只能允许添加一男或一女
47 public synchronized void addSome ()
48 { 
         //添加一男
49     if (i==0)
50      {
51        name="陈奕迅";
52        sex="男性";
53        System.out.println("添加"+name+" "+sex);
54        flag=1;
55       }
56      else if(i==1)
57       {
58           name="王菲";
59           sex="女性";
60           System.out.println("添加"+name+" "+sex);
61           flag=1;
62        }
          //改变下一次,添加为一女
63      i=(i+1)%2;
64 }
65  public synchronized void readSome ()
66   {
67     System.out.println(name+" "+sex);
68     flag=0;
69    }
70 }

 

 

 

12.waitnotifynotifyAll方法

   这三个方法来自Object类中,所以任何的类都可以调用这三个方法,但只能在synchronized 方法中调用

     wait释放cpu执行权,释放锁即放弃监视器,直到其他线程进入同一个监视器并调用notify方法为止.

         调用wait方法的线程处于等待被唤醒状态,而不是等待拿锁的状态。

     notify:唤醒同一个监视器中调用wait的第一个线程。

     notifyAll:唤醒同一个监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。

     Thread.yield() 方法:线程让步,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。

13.线程安全问题原因

    a.多个线程访问出现延迟。

    b.线程随机性

14.同步的前提

    a.同步需要两个或者两个以上的线程。

    b.多个线程使用的是同一个锁。

15.停止线程

      a.定义循环结束标记

         因为线程运行代码一般都是循环,只要控制了循环即可。

      b.使用interrupt(中断)方法。

        该方法是结束线程的冻结状态,使线程回到运行状态中来。

      注:stop方法已经过时不再使用。

16.wait(),sleep()有什么区别?

   wait():释放cpu执行权,释放锁。

 

    sleep():释放cpu执行权,不释放锁。

常见线程名词解释
主线程:JVM调用程序mian()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()setDaemon()方法来判断和设置一个线程是否为后台线程。

 

posted @ 2014-11-15 14:43  beyondbycyx  阅读(203)  评论(0编辑  收藏  举报