`
mymobile
  • 浏览: 182775 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

java 多线程(完全版)

阅读更多

尽管线程对象的常用方法可以通过API文档来了解,但是有很多方法仅仅从API说明是无法详细了解的。我们先来说一下线程对象的几个重要的方法:
    首先我们来说明start() 方法。
    一个线程对象生成后,如果要产生一个执行的线程,就一定要调用它的start()方法.在介绍这个方法时不得不同时说明run方法.其实线程对 象的run方法完全是一个接口回调方法,它是你这个线程对象要完成的具体逻辑.简单说你要做什么就你在run中完成,而如何做,什么时候做就不需要你控制 了,你只要调用start()方法,JVM就会管理这个线程对象让它产生一个线程并注册到线程处理系统中。
    从表面上看,start()方法调用了run()方法,事实上,start()方法并没有直接调用run方法.在JDK1.5以前 start()方法是本地方法,它如何最终调用run方法已经不是JAVA程序员所能了解的.而在JDK1.5中,原来的那个本地start()方法被 start0()代替,另个一个纯JAVA的start()中调用本地方法start0(),而在start()方法中做了一个验证,就是对一个全局变量 (对象变量)started做检验,如果为true,则start()抛出异常,不会调用本地方法start0(),否则,先将该变量设有true,然后 调用start0()。
    从中我们可以看到这个为了控制一个线程对象只能运行成功一次start()方法.这是因为线程的运行要获取当前环境,包括安全 , 父线程的权限, 优先级等条件,如果一个线程对象可以运行多次,那么定义一个static 的线程在一个环境中获取相应权限和优先级,运行完成后它在另一个环境中利用原来的权限和优先级等属性在当前环境中运行,这样就造成无法预知的结果.简单说 来,让一个线程对象只能成功运行一次,是基于对线程管理的需要。
    start()方法最本质的功能是从CPU中申请另一个线程空间来执行 run()方法中的代码,它和当前的线程是两条线,在相对独立的线程空间运行 ,也就是说,如果你直接调用线程对象的run()方法,当然也会执行,但那是 在当前线程中执行,run()方法执行完成后继续执行下面的代码.而调用start()方法后,run()方法的代码会和当前线程并发(单CPU)或并行 (多CPU)执行。
    所以请记住一句话[调用线程对象的run方法不会产生一个新的线程 ],虽然可以达到相同的执行结果,但执行过程和执行效率不同。
    [线程的interrupt()方法,interrupted()和isInterrupted()]
    这三个方法是关系非常密切而且又比较复杂的,虽然它们各自的功能很清楚,但它们之间的关系有大多数人不是真正的了解。
    先说interrupt()方法,它是实例方法,而它也是最奇怪的方法,在java语言中,线程最初被设计为"隐晦难懂"的东西,直到现在它的 语义不没有象它的名字那样准确。大多数人以为,一个线程象调用了interrupt()方法,那它对应的线程就应该被中断而抛出异常,事实中,当一个线程对象调用interrupt()方法,它对应的线程并没有被中断,只是改变了它的中断状态。
    使当前线程的状态变以中断状态,如果没有其它影响,线程还会自己继续执行。
    只有当线程执行到sleep,wait,join等方法时,或者自己检查中断状态而抛出异常的情况下,线程才会抛出异常。
    如果线程对象调用interrupt()后它对应的线程就立即中断,那么interrupted()方法就不可能执行。
    因为interrupted()方法是一个static方法,就是说只能在当前线程上调用,而如果一个线程interrupt()后它已经中断了,那它又如何让自己interrupted()?
    正因为一个线程调用interrupt()后只是改变了中断状态,它可以继续执行下去,在没有调用sleep,wait,join等法或自己抛 出异常之前,它就可以调用interrupted()来清除中断状态(还会原状)interrupted()方法会检查当前线程的中断状态,如果为 "被中断状态"则改变当前线程为"非中断状态"并返回true,如果为"非中断状态"则返回false,它不仅检查当前线程是否为中断状态,而且在保证当 前线程回来非中断状态,所以它叫"interrupted",是说中断的状态已经结束(到非中断状态了) isInterrupted()方法则仅仅检查线 程对象对应的线程是否是中断状态,并不改变它的状态。
    目前大家只能先记住这三个方法的功能,只有真正深入到多线程编程实践中,才会体会到它们为什么是对象方法,为什么是类方法。
    线程到底什么时候才被中断抛出InterruptedException异常,我们将在提高篇中详细讨论。


    [sleep(),join(),yield()方法]
    在现在的环节中,我只能先说明这些方法的作用和调用原则,至于为什么,在基础篇中无法深入,只能在提高篇中详细说明。
    sleep()方法中是类方法,也就是对当前线程而言的,程序员不能指定某个线程去sleep,只能是当前线程执行到sleep()方法时,睡眠指定的时间(让其它线程运行).事实上也只能是类方法,在当前线程上调用.试想如果你调用一个线程对象的sleep()方法,那么这个对象对应的线程如 果不是正在运行,它如何sleep()?所以只有当前线程,因为它正在执行,你才能保证它可以调用sleep()方法。
    原则:[在同步方法中尽量不要调用线程的sleep()方法 ],或者简单说,对于一般水平的程序员你基本不应该调用sleep()方法。
    join()方法,正如第一节所言,在一个线程对象上调用join方法,是当前线程等待这个线程对象对应的线程结束 ,比如有两个工作,工作A要耗时10秒钟,工作B要耗时10秒或更多。我们在程序中先生成一个线程去做工作B,然后做工作A。
    new B().start();//做工作B
    A();//做工作A
    工作A完成后,下面要等待工作B的结果来进行处理.如果工作B还没有完成我就不能进行下面的工作C,所以
    B b=new B();
    b.start();//做工作B
    A();//做工作A
    b.join();//等工作B完成。
    C();//继续工作C。
    原则:[join是测试 其 它工作状态的唯一正确方法],我见过很多人,甚至有的是博士生,在处理一项工作时如果另一项工作没有完成,说让当前工 作线程sleep(x),我问他,你这个x是如何指定的,你怎么知道是100毫秒而不是99毫秒或是101毫秒?其实这就是OnXXX事件的实质,我们不 是要等多长时间才去做什么事,而是当等待的工作正好完成的时候去做。
    yield()方法也是类方法,只在当前线程上调用,理由同上,它主是让当前线程放弃本次分配到的时间片原则 :[不是非常必要的情况下,没有理 由调用它 ].调用这个方法不会提高任何效率,只是降低了CPU的总周期上面介绍的线程一些方法,基于(基础篇)而言只能简单提及.以后具体应用中我会结合 实例详细论述。
    线程本身的其它方法请参看API文档.下一节介绍非线程的方法,但和线程密切相关的两[三]个对象方法:
    [wait(),notify()/notifyAll()]
    这是在多线程中非常重要的方法。
    关于这两个方法,有很多的内容需要说明.在下面的说明中可能会有很多地方不能一下子明白,但在看完本节后,即使不能完全明白,你也一定要回过头来记住下面的两句话:
    [wait(),notify()/notityAll()方法是普通对象的方法(Object超类中实现),而不是线程对象的方法]
    [wait(),notify()/notityAll()方法只能在同步方法中调用]

    [线程的互斥控制]
    多个线程同时操作某一对象时,一个线程对该对象的操作可能会改变其状态,而该状态会影响另一线程对该对象的真正结果.
    这个例子我们在太多的文档中可以看到,就象两个操售票员同时售出同一张票一样.
    线程A 线程B
    1.线程A在数据库中查询存票,发现票C可以卖出
    class="left"2.线程A接受用户订票请求,准备出票.
    3.这时切换到了线程B执行
    4.线程B在数据库中查询存票,发现票C可以卖出
    5.线程B将票卖了出去
    6.切换到线程A执行,线程A卖了一张已经卖出的票
    所以需要一种机制来管理这类问题的发生,当某个线程正在执行一个不可分割的部分时,其它线程不能不能同时执行这一部分.
    象这种控制某一时刻只能有一个线程执行某个执行单元的机制就叫互斥控制或共享互斥(mutual exclusion)
    在JAVA中,用synchornized关键字来实现互斥控制(暂时这样认为,JDK1.5已经发展了新的机制)
    [synchornized关键字]
    把一个单元声明为synchornized,就可以让在同一时间只有一个线程操作该方法.
    有人说synchornized就是一把锁,事实上它确实存在锁,但是是谁的锁,锁谁,这是一个非常复杂的问题.
    每个对象只有一把监视锁(monitor lock),一次只能被一个线程获取.当一个线程获取了这一个锁后,其它线程就只能等待这个线程释放锁才能再获取.
    那么synchornized关键字到底锁什么?得到了谁的锁?
    对于同步块,synchornized获取的是参数中的对象锁:
    synchornized(obj){  //...............  }  线程执行到这里时,首先要获取obj这个实例的锁,如果没有获取到线程只能等待.如果多个线程执行到这里,只能有一个线程获取obj的锁,然后执行 {}中的语句,所以,obj对象的作用范围不同,控制程序不同.
    假如:
    public void test(){ 

 Object o = new Object();                                                                          synchornized(obj){  //...............  }  }                                                   这段程序控制不了任何,多个线程之间执行到Object o = new Object();时会各自产生一个对象然后获取这个对象有监视锁,各自皆大欢喜地执行.
    而如果是类的属性:
    class Test{    Object o = new Object();    public void test(){    synchornized(o){    //...............    }    }}  所有执行到Test实例的synchornized(o)的线程,只有一个线程可以获取到监视锁.
    有时我们会这样:
    public void test(){    synchornized(this){    //...............    }    }  那么所有执行Test实例的线程只能有一个线程执行.而synchornized(o)和synchornized(this)的范围是不同 的,因为执行到Test实例的synchornized(o)的线程等待时,其它线程可以执行Test实例的synchornized(o1)部分,但多 个线程同时只有一个可以执行Test实例的synchornized(this).]
    而对于
    synchornized(Test.class ){    //...............    }  这样的同步块而言,所有调用Test多个实例的线程赐教只能有一个线程可以执行.
    [synchornized方法]
    如果一个方法声明为synchornized的,则等同于把在为个方法上调用synchornized(this).
    如果一个静态方法被声明为synchornized,则等同于把在为个方法上调用synchornized(类.class).

    现在进入wait方法和notify/notifyAll方法.这两个(或叫三个)方法都是Object对象的方法,而不是线程对象的方法.如同锁一样,它们是在线程中调用某一对象上执行的.
    class Test{    public synchornized void test(){    //获取条件,int x 要求大于100;    if(x < 100)    wait();    }    }  这里为了说明方法没有加在try{}catch(){}中,如果没有明确在哪个对象上调用wait()方法,则为this.wait();
    假如:
    Test t = new Test();
    现在有两个线程都执行到t.test();方法.其中线程A获取了t的对象锁,进入test()方法内.
    这时x小于100,所以线程A进入等待.
    当一个线程调用了wait方法后,这个线程就进入了这个对象的休息室(waitset),这是一个虚拟的对象,但JVM中一定存在这样的一个数据结构用来记录当前对象中有哪些程线程在等待.
    当一个线程进入等待时,它就会释放锁,让其它线程来获取这个锁.
    所以线程B有机会获得了线程A释放的锁,进入test()方法,如果这时x还是小于100,线程B也进入了t的休息室.
    这两个线程只能等待其它线程调用notity[All]来唤醒.
    但是如果调用的是有参数的wait(time)方法,则线程A,B都会在休息室中等待这个时间后自动唤醒.
    [为什么真正的应用都是用while(条件)而不用if(条件) ]
    在实际的编程中我们看到大量的例子都是用?
    while(x < 100)
    wait();go();而不是用if,为什么呢?
    在多个线程同时执行时,if(x <100)是不安全 的. 因为如果线程A和线程B都在t的休息室中等待,这时另一个线程使x==100了,并调用notifyAll方法,线程A继续 执行下面的go().而它执行完成后,x有可能又小于100,比如下面的程序中调用了--x,这时切换到线程B,线程B没有继续判断,直接执行go(); 就产生一个错误的条件,只有while才能保证线程B又继续检查一次 .
    [notify/notifyAll方法]
    这两个方法都是把某个对象上休息区内的线程唤醒,notify只能唤醒一个,但究竟是哪一个不能确定,而notifyAll则唤醒这个对象上的休息室中所有的线程.
    一般有为了安全性,我们在绝对多数时候应该使用notifiAll(),除非你明确知道只唤醒其中的一个线程.
    那么是否是只要调用一个对象的wait()方法,当前线程就进入了这个对象的休息室呢?事实中,要调用一个对象的wait()方法,只有当前线程获取了这个对象的锁,换句话说一定要在这个对象的同步方法或以这个对象为参数的同步块中.
    class MyThread extends Thread{                                                            Test t = new Test();                                                                                 public void run(){                                                                                  t.test();                                                                              System.out.println("Thread say:Hello,World!");                                             }    }                                                                                                           public class Test {                                                                                                        int x=0;                                                                                                  public  void test(){                                                                          if(x==0)                                                                                           try{    wait();                                                                      }catch(Exception e){}    }                                                                    public static void main(String[] args) throws Exception{                           new MyThread().start();    }    }                                                           这个线程就不会进入t的wait方法而直接打印出Thread say:Hello,World!.
    而如果改成:
    public class Test {      int x = 0;      public synchornized void test(){      if(x==0)      try{      wait();      }catch(Exception e){}      }      public static void main(String[] args) throws Exception{      new MyThread().start();      }      }

【转载】http://java.chinaitlab.com/line/850221.html

分享到:
评论

相关推荐

    Java多线程编程核心技术_完整版_java_

    Java多线程编程是Java开发中的重要组成部分,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要通过继承Thread类或实现Runnable接口来实现。本教程《Java多线程编程核心技术》将...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    JAVA多线程模式高清版+DEMO

    在"Java多线程设计模式_清晰完整PDF版"文档中,你可能还会学习到如何结合实际场景选择合适的线程模型,如何优化多线程程序,以及如何调试和处理线程安全问题。这些内容对于提升Java并发编程能力非常有帮助,对于开发...

    Java多线程设计模式_清晰完整PDF版 Java多线程设计模式源代码

    本资料集包含了清晰完整的PDF版书籍和源代码,为学习和理解Java多线程设计模式提供了丰富的素材。 首先,我们要理解多线程的核心概念。在Java中,线程是程序执行的最小单位,每个线程都有自己的程序计数器、虚拟机...

    Java多线程技术精讲

    Java多线程技术是Java编程中的重要组成部分,它允许程序同时执行多个任务,...在实际开发中,结合《Java多线程编程核心技术_完整版 带书签目录.pdf》这样的学习资料,辅以实践,将有助于你更好地驾驭Java多线程的世界。

    Java多线程编程技术

    《Java多线程编程核心技术》建议猿友们读两遍,因为其写得没有那么抽象,第一遍有些概念不是很理解,可以先跳过并记录起来,第一遍阅读的目的主要是了解整个架构。第二遍再慢慢品味,并贯穿全部是指点来思考,并将...

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    java线程第二版中英文 java线程第二版中英文 线程并不是新的概念:许多操作系统和语言都支持它们。在Java出现以前,似乎人人都在谈论线程,却很少有人使用它。用线程编程是技巧性很强的且不可移植。 而在Java中却...

    Java多线程详解(超详细)_狂神说笔记完整版_项目代码_适合小白随课程学习

    Java多线程详解 在Java编程中,多线程是一种重要的技术,它使得程序能够同时执行多个任务,提高系统的效率和响应性。本教程将详细讲解Java中的多线程概念,包括线程的创建、状态、同步以及高级主题,旨在帮助初学者...

    基于Java多线程和GUI的贪吃蛇

    在本项目"基于Java多线程和GUI的贪吃蛇"中,开发者利用了Java的强大功能,构建了一个具有用户友好的图形用户界面(GUI)的游戏,并融入了多线程技术来实现游戏的流畅运行。以下是关于这个项目所涉及的知识点的详细...

    java多线程.pdf

    Java多线程是Java编程中不可或缺的部分,它允许程序同时执行多个任务,提高了程序的并发性和效率。在Java中,实现多线程主要有两种方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: - 当你定义一个类...

    java多线程核心技术_完整版

    Java多线程核心技术是Java开发中的重要组成部分,它关乎到...这个"Java多线程编程核心技术_完整版.pdf"教程应该涵盖了上述所有知识点,深入学习后,你将能够熟练地运用Java进行多线程编程,解决实际开发中的并发问题。

    图解java多线程设计模式-结城浩-完整高清带书签版本

    《图解Java多线程设计模式》是由日本著名技术作家结城浩编著的一本深入探讨Java多线程编程的经典著作。这本书以清晰易懂的方式,结合丰富的图表和实例,全面解析了Java多线程开发中的关键概念、设计模式以及实践技巧...

    JAVA多线程设计模式(带完整书签清晰扫描版)

    《JAVA多线程设计模式》中包含JAVA线程的介绍导读,12个重要的线程设计模式和全书总结以及丰富的附录内容。每一章相关线程设计模式的介绍,都举一反三使读者学习更有效率。最后附上练习问题,让读者可以温故而知新,...

    Java多线程编程环境中单例模式的实现

    ### Java多线程编程环境中单例模式的实现 #### 概述 单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在Java中,单例模式的应用非常广泛,特别是在资源管理、日志记录、...

    基于环境的java多线程行为比较分析

    ### 基于环境的Java多线程行为比较分析 #### 概述 Java的多线程机制在软件开发中占据着重要的地位,它能够显著提高应用程序的性能和响应速度,尤其是在多核处理器环境中。然而,Java多线程的行为表现会受到多种...

    java多线程编程技术

    Java多线程编程技术 Java多线程编程技术是Java编程语言中的一项高级技术,其主要目的是让开发者能够编写出能够同时执行多个操作的程序,以提高程序的执行效率和响应性。在多核处理器日益普及的今天,合理运用多线程...

    Java线程基础教程完整版

    本教程将深入讲解Java线程的基础知识,帮助你掌握多线程编程的核心技能。 1. **线程的创建** - **继承Thread类**:创建一个新的类,该类继承自Thread类,然后重写它的`run()`方法。通过创建此类的实例并调用其`...

    Java实现多线程下载源代码

    总的来说,这个项目是一个很好的实践示例,它不仅展示了Java多线程编程的基本概念,还结合了图形用户界面的设计,帮助初学者理解和掌握这两个重要的编程技能。通过分析和运行这个源代码,开发者不仅可以学习到多线程...

Global site tag (gtag.js) - Google Analytics