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

来源:互联网 发布:c语言 读取jpg 源代码 编辑:程序博客网 时间:2024/05/16 17:13
----------------------android培训java培训、期待与您交流! --------------------

多线程

 

1、多线程的阐述

      几乎所有的操作系统都支持同时运行多个任务,一个任务通常都是一个程序,每个运行中的任务就是一个进程。当一个程序运行的时候,内部可能包含了多个程序执行流,这每一个程序执行流就是一个个单独的线程。线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须拥有一个父进程。线程可以完成一定的任务,他可以和其他线程共享父进程中的资源,相互协作来完成各自的任务。但是线程的运行时独立的,他不知道同一个进程中还有其他的线程的存在,所以他们的运行就是抢占式的,我们在创建线程的时候必须处理好他们的资源共享的问题。
      多线程的优势:进程间不能共享内存,但是线程间共享内存却是非常容易的。  由于线程的并发性优势,所以使用多线程的效率会高很多

2、线程创建的两种方式

    2.1  继承Thread类,复写run()方法。调用start()方法,启动线程。

 
  class ThreadDemo    {    public static void main(String[] args)    {        new MyThread().start();//创建并启动第一个线程        new MyThread().start();//创建并启动第二个线程    }    }    class MyThread extends Thread    {    public void run()    {        //需要线程执行的代码    }    }


        注意:启动线程是调用Thread的start()方法,他的底层会自动调用Thread的run()方法。

   2.2  实现Runnable接口,复写run()方法,将其传入Thread的构造函数,启动线程

 
  class ThreadDemo2    {    public static void main(String[] args)    {        MyThread my=new MyThread();//创建线程资源对象        new MyThread(my).start();//启动第一个线程        new MyThread(my).start();//启动第二个线程    }    }    class MyThread2 implements Runnable    {    public void run()    {        //需要线程执行的代码    }    }


    2.3  两种线程的对比以及其生命周期
     我们已经熟悉了线程的两种创建方式,那么这两种线程各自的优缺点就是:实现Runnable接口,还可以去继承其他类,因为java不支持多继承,但是支撑多实现。所以避免了单继承的局限性,在这种情况下,可以让多个线程共享同一个资源,很好的体现了面向对象的思想。
    当线程被创建并启动以后,他既不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期内,他要经过新建、就绪、运行、阻塞和死忙五种状态。尤其是启动之后,进行着线程之间的切换。线程状态的不同决定着当前线程的执行情况。我们可以通过线程的各自方法,让线程处于不同的状态,但是值得注意的就是:  当一个线程死了之后,就不能再用start()方法再来开启他。可以通过isAlive()方法来判断是否死亡。

3、线程的同步---安全问题的解决。

          在多个线程并发的访问同一个资源的时候,就会引发安全问题。关于线程安全问题,最显著的例子就是毕老师所举的火车卖票的例子。
      为了解决这个问题,java的多线程支持引入了同步监听器来解决这个问题,就是同步代码块的出现解决了这个问题。他的格式就是synchronized(Object obj){//需要被同步的代码},这个式子中的obj就是所说的监听器。同步监听器的意思就是:线程开始执行同步代码块前必须先获得对同步监听器的锁定。
      同步方法的诞生:与同步代码块相对应的就是java的多线程的安全技术还提供了同步方法,同步方法就是使用关键字synchronized来修饰要监听的方法,对于同步方法来说没有必要来指定监听器对象,因为此时监听的对象就是this,就是对象自身。
      从JDK1.5之后,java提供了另外一种线程同步的机制:它通过显示定义同步锁对象来实现同步,在这种机制下,同步锁应该使用Lock对象充当。
      通常认为:Lock提供了比synchronized更广泛的锁定操作,并且可以支持多个相关的Condition对象。Lock是控制线程对共享资源进行访问的工具。
      使用Lock的一般方法就是先创建Lock对象,在获得condition对象,调用condition的方法进行资源的访问限制。
      例如:Lock lock=new ReadWriteLock();
                  Condition condition=lock.newCondition()
      在调用lock.lock();和lock.unLock();这两个方法限制共享资源。
      在同步中可以调用condition.await();和condition.signal();这两个方法唤醒持有所的对象。
      需要注意的就是:在最后必须释放所资源,也就是说将lock.unLock();必须放在finally语句里面。
      在JDK1.5的新特性中出现了ArrayBlockingQuece它里面提及的方法可以实现资源的依次访问。

4、JDK1.5线程的新特性

   4.1  Callable和Future的运用
 

   import java.util.concurrent.Callable;    import java.util.concurrent.ExecutionException;    import java.util.concurrent.Executors;    import java.util.concurrent.Future;    public class CallableandFuture {    public static void main(String[] args) {        Future future=Executors.newSingleThreadExecutor().submit(            new Callable<String>() {                public String call()                {                    return "你好";                }            }        );        System.out.println("拿去结果");        try {            System.out.println(future.get());        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (ExecutionException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }     }


      其实,Callable接口和Runnable接口是差不多的,只不过Callable接口的call方法有返回值,Runnable接口的run方法没有返回值,他们都可以当做参数用Executes去启动线程,但是我们可以用Future的get方法等待线程执行完毕后的结果。

   4.2  Executors的运用

       因为系统启动一个新线程的成本还是比较高的,因为它涉及到与操作系统的相互交互。由此,JDK1.5开启了一个     工厂类来产生线程池---Executors。
     使用线程池来执行线程任务的步骤如下:
     调用Executors类的静态工厂方法创建一个ExecutorsServerce对象,该对象代表着一个线程池。
     创建Callable或者Runnable类的实现类作为线程执行任务。
     调用ExecutorsServerce对象的submit方法来提交Runnable实例或者Callable实例。
     当不像提交任务时候,可以使用shutdown方法来结束线程,但是shutdown方法在执行前会将当前线程的任务执行完,当想立即结束线程的时候,就可以使用shutdownNow()方法。

    4.3  包装线程不安全的集合

           JDK1.5还提供了ConcurrentHashMap、ConcurrentLinkedQueue这两个类,它支持线程的并发访问集合。
     当然,集合工具类Collections还提供喜多同步方法可以讲线程不同步的集合编程并发访问安全的。
       

5、线程之间的通信---等待唤醒机制

       为了实现线程之间的相互唤醒机制,我们特具毕老师经典的例子
 
   //实现输入一次,打印一次  交替进行     import java.lang.*;     class ThreadDemo     {    public static void main(String[] args)    {        Res r=new Res();        Inne i=new Inne(r);        Out o=new Out(r);        new Thread(i).start();        new Thread(o).start();    }     }     class Res     {    String name;    int age;     boolean flag=false;         }     class Inne implements Runnable     {    private Res r;    Inne(Res r){this.r=r;}    public void run()    {  int x=0;        while(true)        {            synchronized(r)            {                     if(r.flag)                try{r.wait();}catch(Exception   e){}                   if(x==0)                {r.name="张三";                r.age=30;}                else                {                    r.name="lisi";                            r.age=90;                }                x=(x+1)%2;                r.flag=true;                r.notify();            }        }            }     }     class Out implements Runnable    {    private Res r;    Out(Res r){this.r=r;}    public void  run()    {        while(true)        {           synchronized(r)                    {            if(!(r.flag))                try{r.wait();}catch(Exception  e){}            else                System.out.println(r.name+r.age);            r.flag=false;            r.notify();                        }        }    }     }


        以上就是传统的等待唤醒机制。

6、总结

      本文总结了创建线程的传统方式以及新技术的特点以及开启线程的方法。
      多线程是java中特别重要的技术,特别是1.5以后新技术的出现,解决了很多传统很难实现的问题。我们应该重点掌握




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

原创粉丝点击