2即使线程t的优先级高,在调用start时,也不会保证线程已经真实的启动。os只是把他放到了线程队列中去排队。而当前线程继续执行。
SwingAPI的设计目标是强大 灵活和易用 非凡地 我们希望能让程序员们方便地建立新的Swing组件 不论是从头开始还是通过扩展我们所提供的一些组件 出于这个目的 我们不要求Swing组件支持多线程访问 相反 我们向组件发送请求并在单一线程中执行请求 本文讨论线程和Swing组件 目的不仅是为了帮助你以线程安全的方式使用SwingAPI 而且解释了我们为什么会选择现在这样的线程方案 本文包括以下内容
单线程规则 Swing线程在同一时刻仅能被一个线程所访问 一般来说 这个线程是事件派发线程 规则的例外 有些 *** 作保证是线程安全的 事件分发 假如你需要从事件处理或绘制代码以外的地方访问UI 那么你可以使用SwingUtilities类的invokeLater要求在事件派发线程中执行某些代码 这个方法会立即返回 不会等待代码执行完毕 invokeAndWait行为与invokeLater类似 除了这个方法会等待代码执行完毕 一般地 你可以用invokeLater来代替这个方法 下面是一些使用这几个API的例子 请同时参阅《TheJavaTutorial》中的 BINGOexample 尤其是以下几个类 CardWindow ControlPane Player和OverallStatusPane
使用invokeLater方法你可以从任何线程调用invokeLater方法以请求事件派发线程运行特定代码 你必须把要运行的代码放到一个Runnable对象的run方法中 并将此Runnable对象设为invokeLater的参数 invokeLater方法会立即返回 不等待事件派发线程执行指定代码 这是一个使用invokeLater方法的例子
RunnabledoWorkRunnable=newRunnable }
SwingUtilities invokeLater使用invokeAndWait方法invokeAndWait方法和invokeLater方法很相似 除了invokeAndWait方法会等事件派发线程执行了指定代码才返回 在可能的情况下 你应该尽量用invokeLater来代替invokeAndWait 假如你真的要使用invokeAndWait 请确保调用invokeAndWait的线程不会在调用期间持有任何其他线程可能需要的锁
这是一个使用invokeAndWait的例子
voidshowHelloThereDialogthrowsException }SwingUtilities invokeAndWait}
类似地 假设一个线程需要对GUI的状态进行存取 比如文本域的内容 它的代码可能类似这样
voidprintTextField throwsException }SwingUtilities invokeAndWaitSystem out println}
假如你能避免使用线程 最好这样做 线程可能难于使用 并使得程序的debug更困难 一般来说 对于严格意义下的GUI工作 线程是不必要的 比如对组件属性的更新 不管怎么说 有时候线程是必要的 下列情况是使用线程的一些典型情况 执行一项费时的任务而不必将事件派发线程锁定 例子包括执行大量计算的情况 会导致大量类被装载的情况 和为网络或磁盘I/O而阻塞的情况 重复地执行一项 *** 作 通常在两次 *** 作间间隔一个预定的时间周期 要等待来自客户的消息 你可以使用两个类来帮助你实现线程 SwingWorker 创建一个后台线程来执行费时的 *** 作 Timer 创建一个线程来执行或多次执行某些代码 在两次执行间间隔用户定义的延迟 使用SwingWorker类SwingWorker类在SwingWorker java中实现 这个类并不包含在Java的任何发行版中 所以你必须单独下载它 SwingWorker类做了所有实现一个后台线程所需的肮脏工作 虽然许多程序都不需要后台线程 后台线程在执行费时的 *** 作时仍然是很有用的 它能提高程序的性能观感
SwingWorkersanexampleofusingSwingWorker 要使用SwingWorker类 你首先要实现它的一个子类 在子类中 你必须实现construct方法还包含你的长时间 *** 作 当你实例化SwingWorker的子类时 SwingWorker创建一个线程但并不启动它 你要调用你的SwingWorker对象的start方法来启动线程 然后start方法会调用你的construct方法 当你需要construct方法返回的对象时 可以调用SwingWorker类的get方法 这是一个使用SwingWorker类的例子
//在main方法中 finalSwingWorkerworker=newSwingWorker }worker start //在动作事件处理方法中 JOptionPane showMessageDialog)
当程序的main方法调用start方法 SwingWorker启动一个新的线程来实例化ExpensiveDialogComponent main方法还构造了由一个窗口和一个按钮组成的GUI 当用户点击按钮 程序将阻塞 假如必要 阻塞到ExpensiveDialogComponent创建完成 然后程序显示一个包含ExpensiveDialogComponent的模式对话框 你可以在MyApplication java找到整个程序 使用Timer类Timer类通过一个ActionListener来执行或多次执行一项 *** 作 你创建定时器的时候可以指定 *** 作执行的频率 并且你可以指定定时器的动作事件的监听者 启动定时器后 动作监听者的actionPerformed方法会被调用来执行 *** 作 定时器动作监听者定义的actionPerformed方法将在事件派发线程中调用 这意味着你不必在其中使用invokeLater方法 这是一个使用Timer类来实现动画循环的例子
publicclassAnimatorApplicationTimer extendsJFrameimplementsActionListener publicvoidstartAnimationelse } publicvoidstopAnimation publicvoidactionPerformed }
在一个线程中执行所有的用户界面代码有这样一些优点 组件开发者不必对线程编程有深入的理解 像ViewPoint和Trestle这类工具包中的所有组件都必须完全支持多线程访问 使得扩展非常困难 尤其对不精通线程编程的开发者来说 最近的一些工具包如SubArctic和IFC 都采用和Swing类似的设计 事件以可预知的次序派发 invokeLater排队的runnable对象从鼠标和键盘事件 定时器事件 绘制请求的同一个队列派发 在一些组件完全支持多线程访问的工具包中 组件的改变被变化无常的线程调度程序穿插到事件处理过程中 这使得全面测试变得困难甚至不可能 更低的代价 尝试小心锁住临界区的工具包要花费实足的时间和空间在锁的治理上 每当工具包中调用某个可能在客户代码中实现的方法时 工具包都要保存它的状态并释放所有锁 以便客户代码能在必要时获得锁 当控制权交回到工具包 工具包又必须重新抓住它的锁并恢复状态 所有应用程序都不得不负担这一代价 即使大多数应用程序并不需要对GUI的并发访问 这是的SubArcticJavaToolkit的对在工具包中支持多线程访问的问题的描述 我们的基本信条是 当设计和建造多线程应用程序 尤其是那些包括GUI组件的应用程序时 必须保证极端小心 线程的使用可能会很有欺骗性 在许多情况下 它们表现得能够极好的简化编成 使得设计 专注于单一任务的简单自治实体 成为可能 在一些情况下它们的确简化了设计和编码 然而 在几乎所有的情况下 它们都使得调试 测试和维护的困难大大增加甚至成为不可能 无论大多数程序员所受的练习 他们的经验和实践 还是我们用来帮助自己的工具 都不是能够用来对付非决定论的 例如 全面测试在bug依靠于时间时是几乎不可能的 尤其对于Java来说 一个程序要运行在许多不同类型的机器的 *** 作系统平台上 并且每个程序都必须在抢先和非抢先式调度下都能正常工作 由于这些固有的困难 我们力劝你三思是否绝对有使用线程的必要 尽管如此 有些情况下使用线程是必要的 所以subArctic提供了一个线程安全的访问机制
lishixinzhi/Article/program/Java/gj/201311/27616所有Java.lang.Object的子类都从Object类继续了3个wait和1个notity()及1个notifyAll()方法。具体的区别看相关的API。有时或者你还想使用sleep方法,notify方法的调用,会使当前线程进行阻塞状态,三个notify的区别,有的是直接加入到阻塞队列中,有的是阻塞一定的时间,但是不论哪种,没有本质区别,因为一旦加入到了阻塞队列,线程的优先级并不会改变,也就是说,只要你不去调用唤醒,注意是唤醒进程的notify()或者notifyAll()方法,线程会一直阻塞,除非你调用了,线程才会从阻塞状态变成可运行状态(runnable),注意是 可运行,并不是说立即马上就会执行,对于处于可运行状态的多个线程来说,由JVM根据当前线程池当中的可运行状态的线程的优先级来决定,Java中的线程执行是完成根据优先级抢占式执行的,也就是说,高优先级的线程会首先获得执行,当然正在运行的线程也会被比他高的线程把中断,至于中断的具体细节需要你写相关的代码测试,JVM实现这个功能的代码是不开源的。可能最让人迷惑的就是同优先级,或者默认优先级线程的执行顺序,我来告诉你,同优先级的顺序在JVM规范里是这么讲的:同优先级的线程的执行顺序是不确定的,JVM本身不支持多线程,JDK1.5时,当前1.6.24的版本不太清楚,除非当前JVM所在的 *** 作系统支持多线程,就像现在的多核系统,JVM有可能会支持多线程的处理,注意JVM不具有跨平台性。。。,所以在多核下的多线程是一个非常值得测试的东西。除非当前的 *** 作系统技术多线程,JVM才有可能支持多线程的处理,注意是有可能。并且JVM规范里说了,Java下所有在线程上的 *** 作,程序的正确性永远不要依赖于线程的优先级。也就是说不要希望通过修改线程的优先级来决定程序执行顺序的正确性。解决这个问题最好的办法,我用的还是同步和互斥。通过他来共享 *** 作。主要原因还是只有一个,同优先级线程并发执行是完全不可猜测的。完全随机,因素包括当前JVM的状态,可以这么想象一下,如果当前有多个线程需要执行,在JVM有限的资源上,不同的线程所要的资源和使用这些资源的时间是不同的,这也就决定了,在JVM的设计上,会根据一定的算法,来决定这些线程执行的顺序,否则同时装载几个需要大量资源的线程将导致JVM崩溃等等,其实是没有太大意义的,也顶多只是让当前线程进行可执行状态,进行等待队列,等待执行罢了。 希望你能明白。好长时间不看书,完全是看到这个问题,想起来的,可能有点乱。如果想再深入明白,包括线程具体是怎么实现的,可以找JVM规范相关的书看看。 对线程最好的学习,就是写程序测试线程,多设断点 ,来跟踪,通过每次执行的不同的可能的结果来经验化的学习。 最后说一句:notify() notifiAll()只会让线程从阻塞状态变成可执行状态,runnable 到 run状态,状态的变化是由JVM来完成。换句话说,就是程序里的run()方法的调用。这种随机执行其实完全是因为特定的算法,和当前JVM的运行状态。换句话说,其实并不是真的随机。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)