`
longgangbai
  • 浏览: 7339557 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java Thread的一点知识(II)

 
阅读更多

stop() 和 suspend() 方法为何不推荐使用?

 


 

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记线程的stop方法,以后我们再也不要说“停止线程”了。而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果 很难检查出真正的问题所在。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此 时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就 会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用 wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

sleep() 和 wait() 有什么区别? 

 

 

sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来的线程具有更高的优先级,(b)正在运行的线程因为其它原因而阻塞。

waitObject类的方法,对此对象调用wait方法导致本线程放弃对象锁释放当前线程锁定的任何对象。进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

sleep()方法是本地方法,属于Thread类,它有两种定义:

public static native void sleep(long millis) throws InterruptedException;  

public static void sleep(long millis, int nanos) throws InterruptedException { 

    //other code 

} 

其中的参数millis代表毫秒数(千分之一秒),nanos代表纳秒数(十亿分之一秒)。这两个方法都可以让调用它的线程沉睡(停止运行)指定的时间,到了这个时间,线程就会自动醒来,变为可运行状态(RUNNABLE),但这并不表示它马上就会被运行,因为线程调度机制恢复线程的运行也需要时间。调用sleep()方法并不会让线程释放它所持有的同步锁;而且在这期间它也不会阻碍其它线程的运行。上面的2个方法都声明抛出一个 InterruptedException类型的异常,这是因为线程在sleep()期间,有可能被持有它的引用的其它线程调用它的 interrupt()方法而中断。中断一个线程会导致一个InterruptedException异常的产生,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。

为了更好地理解interrupt()效果,我们来看一下下面这个例子:

public class InterruptTest { 

    public static void main(String[] args) { 

        Thread t = new Thread() { 

            public void run() { 

                try { 

                    System.out.println("我被执行了-sleep()方法前"); 

                    // 停止运行10分钟 

                    Thread.sleep(1000 * 60 * 60 * 10); 

                    System.out.println("我被执行了-sleep()方法后"); 

                } catch (InterruptedException e) { 

                    System.out.println("我被执行了-catch语句块中"); 

                } 

                System.out.println("我被执行了-try{}语句块后"); 

            } 

        }; 

        // 启动线程 

        t.start(); 

        // sleep()结束前中断它 

        t.interrupt(); 

    } 

} 

运行结果:

我被执行了-sleep()方法前

我被执行了-catch语句块中

我被执行了-try{}语句块后

 

wait()方法也是本地方法,属于Object类,有三个定义:

public final void wait() throws InterruptedException { 

    //do something 

}  

 

 

sleep()和yield()的区别

1) sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。

2) sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会

public class ThreadTest implements Runnable{

public void run(){

for(int k=0;k<10;k++){

if(k == 5 && Thread.currentThread().getName().equals("t1")){

Thread.yield();

}

System.out.println(Thread.currentThread().getName()+ " : " + k);

}

}

public static void main(String[] args) {

Runnable r = new ThreadTest();

Thread t1 = new Thread(r,"t1");

Thread t2 = new Thread(r,"t2");

t1.setPriority(Thread.MAX_PRIORITY);

t2.setPriority(Thread.MIN_PRIORITY);

t1.start();

t2.start();

}

}

输出结果:

t1 : 0

t1 : 1

t1 : 2

t1 : 3

t1 : 4

t1 : 5

t1 : 6

t1 : 7

t1 : 8

t1 : 9

t2 : 0

t2 : 1

t2 : 2

t2 : 3

t2 : 4

t2 : 5

t2 : 6

t2 : 7

t2 : 8

t2 : 9

多次运行这个程序,输出也是一样。这说明:yield()方法不会使不同优先级的线程有执行的机会。

 

 

区分interupt()和 notify().

isInterrupted()interrputed()方法的区别

isInterrupted方法是实例方法,interrupted方法是静态方法

Thread.currentThread().isInterrupted()

Thread.interrupted()

首先说明:wait(),notify(),notifyAll()这些方法由java.lang.Object 类提供,而上面讲到的方法都是由java.lang.Thread 类提供(Thread类实现了Runnable 接口)。wait(),notify(),notifyAll()这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized 语句块内使用这三个方法。先看下面了例子:

示例12

public class ThreadTest implements Runnable{

public static int shareVar = 0;

public synchronized void run(){

if(shareVar == 0){

for(int i=0;i<10;i++){

shareVar++ ;

if(shareVar == 5){

try{

this.wait();

}catch(Exception e){}

}} }

if(shareVar != 0){

System.out.print(Thread.currentThread().getName());

System.out.println(" shareVar = " + shareVar);

this.notify();

}

}

public static void main(String[] args){

Runnable r = new ThreadTest();

Thread t1 = new Thread(r,"t1");

Thread t2 = new Thread(r,"t2");

t1.start();

t2.start();

}}

运行结果:

t2 shareVar = 5

t1 shareVar = 10

t1线程最先执行。由于初始状态下shareVar 0t1 将使shareVar 连续加1,当shareVar的值为5 时,t1 调用wait()方法,t1 将处于休息状态,同时释放锁标志。这时t2 得到了锁标志开始执行,shareVar 的值已经变为5,所以t2 直接输出shareVar 的值,然后再调用notify() 方法唤醒t1t1 接着上次休息前的进度继续执行,把shareVar 的值一直加到10由于此刻shareVar 的值不为0,所以t1 将输出此刻shareVar的值,然后再调用notify()方法,由于此刻已经没有等待锁标志的线程,所以此调用语句不起任何作用。

 

 

Wait()与notify()

 

 

wait()->本线程进入等待队列,只能通过别的线程的notify()notifyall()唤醒  

  notify()->随机地从等待队列删除一个线程,也就是说,该线程进入执行状态  

  notifyall()->删除所有在等待队列中的线程,按线程的优先级执行  

  不推荐使用notify():因为这是随机地从等待队列中取一个线程执行,我们不能通过设定优先级进行控制,如果,随机抽取的线程不能往前执行,这就有可能产生死锁,所以,建议使用notifyall()  

如:

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();

    }

}

结果这个线程就不会进入twait方法而直接打印出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();

      }

}

我们就可以看到线程一直等待,注意这个线程进入等待后没有其它线程唤醒,除非强行退出JVM环境,否则它一直等待.所以请记住:[线程要想调用一个对象的wait()方法就要先获得该对象的监视锁,而一旦调用wait()后又立即释放该锁]

public class ThreadA{   

    public  static void  main(String[] args){  

      ThreadB   b=new   ThreadB();  

      b.start(); //这里只是将线程b处于可运行状态,并不会立刻执行,实际上是先执行主线(进)程,再去执行线程b,如果主线程运行时间过长的话,则会在期间执行b 

      System.out.println("b   is   start....");  

      synchronized(b){ //定义一个同步块,使用b作为资源锁。对于wait方法必须有的     

        try{  

          System.out.println("Waiting   for   b   to   complete...");  

          b.wait();//临时释放锁,并阻塞当前线程。让其他使用同一把锁的线程ThreadB有机会执行。 注意:直接写wait()是不对的。 

          System.out.println("waiting....");  

        }catch(InterruptedException e){}  

     }  

      System.out.println("Total   is   :"+b.total);

     }   

  }  

  class  ThreadB extends Thread{  

  int   total;  

  public   void   run() {  

      System.out.println("ThreadB   is   running   ....");  

      synchronized(this){ //必须有

      System.out.println("ThreadB   is   excuting   for   statement..");  

      for(int i=0;i<5;i++){  

         total+=i;  

         System.out.println("total   is   "+total);  

      }  

      notify();  //在已经获得了一个对象的锁的前提下,调用notify()会通知线程调度程序,唤醒其它等待这个锁的线程队列中的线程,notifyAll()唤醒所有这类线程。

    }   //end for synchronized

  } 

  }

运行结果:

b   is   start....

Waiting   for   b   to   complete...

ThreadB   is   running   ....

ThreadB   is   excuting   for   statement..

total   is   0

total   is   1

total   is   3

total   is   6

total   is   10

waiting....

Total   is   :10

 

程序中必须同时满足以下四个条件才会引发死锁:

互斥(Mutual exclusion):线程所使用的资源中至少有一个是不能共享的,它在同一时刻只能由一个线程使用。

持有与等待(Hold and wait:至少有一个线程已经持有了资源,并且正在等待获取其他的线程所持有的资源。

非抢占式(No pre-emption:如果一个线程已经持有了某个资源,那么在这个线程释放这个资源之前,别的线程不能把它抢夺过去使用。

循环等待(Circular wait):假设有N个线程在运行,第一个线程持有了一个资源,并且正在等待获取第二个线程持有的资源,而第二个线程正在等待获取第三个线程持有的资源,依此类推……第N个线程正在等待获取第一个线程持有的资源,由此形成一个循环等待。

举例:

1.未发生死锁

public class Test {   

   static Object o1 = new Object();//静态对象

   static Object o2 = new Object();   

    public static void main(String[] args) {        

         synch synch1 = new synch();

         synch synch2 = new synch();

         synch1.a=0;

         synch2.a=2;

         Thread t1 = new Thread(synch1);

         Thread t2 = new Thread(synch2);

         t1.start();

         t2.start();

    }  

}//Test类结束

class synch implements Runnable{   

    int a;

    public void run() {

        System.out.println(a);

        if(a == 0){

          try {

            synchronized (Test.o1) {//对类Testt中的静态对象同步               

                Thread.sleep(500);

            } //synchronized是对o1的对象锁,500毫秒以后会释放掉对o1的锁,所以不会发生死锁         

            synchronized (Test.o2) {

                System.out.println("aaaa");

            }

        } catch (InterruptedException ex) {}

       }else

           try {

                synchronized (Test.o2) {

                   Thread.sleep(500);

            }            

            synchronized (Test.o1) {

                System.out.println("aaba");

            }

        } catch (InterruptedException ex) {}

       

    }

}

运行后显示

0

2

aaba

aaaa

没有发生死锁

2.发生了死锁

修改如下:

public void run() {

    System.out.println(a);

    if(a == 0){

        try {

            synchronized (Test.o1) { //已获取o1对象锁

                Thread.sleep(500);

                System.out.println("Try to get o2");

                synchronized (Test.o2) {  //未释放o1(同步块中的语句执行完以后才释放该对象的锁),尝试获取o2,被挂起

                    System.out.println("aaaa");

                }

            }

        } catch (InterruptedException ex) {}

    } else {

        try {

            synchronized (Test.o2) { //已获取o2的对象锁

                Thread.sleep(500);

                System.out.println("Try to get o1");

                synchronized (Test.o1) {  //未释放o2,尝试获取o1,也被挂起,死锁

                    System.out.println("aaba");

                }

            }

        } catch (InterruptedException ex) {}

    }

}

结果:

0

2

Try to get o2

Try to get o1

另一个例子:

class Friendly {

    private Friendly partner;

    private String name;

    public Friendly(String name) {

        this.name = name;

    }

    public synchronized void hug() {

        System.out.println(Thread.currentThread().getName()+" in " + name + ".hug() trying to invoke " + partner.name +".hugBack()");

        partner.hugBack();

    }

    private synchronized void hugBack() {

        System.out.println(Thread.currentThread().getName()+" in " + name + ".hugBack()");

    }

    public void becomeFriend(Friendly partner) {

        this.partner = partner;

    }

public static void main(String[] args) {

    final Friendly jareth = new Friendly("jareth");

    final Friendly cory = new Friendly("cory");

    jareth.becomeFriend(cory);

    cory.becomeFriend(jareth);

    new Thread(new Runnable() {

        public void run() { jareth.hug(); }

    }, "Thread1").start();

 

    new Thread(new Runnable() {

        public void run() { cory.hug(); }

    }, "Thread2").start();

}

}

结果:

Thread1 in jareth.hug() trying to invoke cory.hugBack()

Thread1 in cory.hugBack()

Thread2 in cory.hug() trying to invoke jareth.hugBack()

Thread2 in jareth.hugBack()

分享到:
评论

相关推荐

    一点课堂 JAVA核心知识点整理.zip

    "一点课堂 JAVA核心知识点整理.zip"这个压缩包文件包含了关于Java编程的关键概念和技能的详细总结,非常适合初学者和有经验的开发者进行学习和复习。 首先,Java的核心知识点包括基础语法。这包括变量声明、数据...

    Java实现12306查票

    Java提供了Thread类和Runnable接口来实现多线程,同时JavaFX也提供了Task和Service类来更方便地处理FX应用程序中的后台任务。 7. **事件驱动编程**: JavaFX基于事件驱动模型,用户与界面的交互(如点击按钮)会触发...

    Java练习题库(含答案及解析).pdf

    Java提供了Thread类和Runnable接口来创建多线程程序。 以上便是根据提供的Java练习题库内容整理出的知识点概述,掌握了这些基础知识点,对进一步学习Java的高级特性和开发实际应用程序都有很大帮助。

    java基础知识大总结

    ### Java基础知识大总结 #### 一、基础概念与特性 1. **Null的打印结果**:在Java中,如果尝试打印一个null值的对象引用,输出结果将会是`null`。 2. **字符串常量的拼接问题**:当在字符串中出现如`'10'`这样的...

    JAVA 火车售票系统

    Java的线程机制可以帮助你实现这一点,如创建Thread类的子类或者使用Runnable接口。 6. **IO流**:用于读写数据,如保存和加载火车信息、用户购票记录等。Java的FileInputStream和FileOutputStream可以用来读写文件...

    Java code Java code

    标签也进一步确认了这一点,同样显示为"Java code Java code"。下面我们将详细讨论Java编程语言的关键知识点,并可能在这些文件中找到的相应内容。 1. **Java基础知识**:Java是一种面向对象的、跨平台的编程语言,...

    java mp3小程序代码

    `Thread`或`Runnable`接口可用于实现这一点。 4. **用户界面(UI)设计**:如果程序具有图形用户界面,可能需要使用Java Swing或JavaFX来创建按钮、进度条等控件,实现与用户的交互。 5. **事件监听**:在UI中,...

    java入门非常的浅显易懂

    "java入门非常的浅显易懂"这个标题暗示了我们将要探讨的内容是针对新手的Java基础知识,而描述中的“很好的入门书”进一步确认了这一点,意味着我们将深入浅出地讲解Java的基本概念。 1. **Java简介**:Java是由Sun...

    JAVA技术500篇

    以上是Java技术500篇可能涉及的主要知识点,每一点都值得深入学习和实践,以提升你的Java编程能力。在实际开发中,还需要结合设计模式、最佳实践以及不断涌现的新技术和框架,如Spring Boot、MyBatis、Docker、...

    JAVA多线程教材

    1. **Java多线程基础知识**:Java多线程允许程序同时执行多个任务,这是通过创建和管理线程实现的。Java提供了两种创建线程的方式:继承Thread类和实现Runnable接口。线程的状态包括新建、就绪、运行、阻塞和终止,...

    java基础教程(强烈推荐)

    Java通过内置的Thread类和它的子类来支持多线程编程,提供了一套丰富的API来处理多线程环境下的复杂问题。 Java语言的另一个重要特性是安全性。Java通过字节码验证器以及类加载器来保证应用程序的安全性。字节码...

    JAVA的MPEG播放器

    Java的Thread类和ExecutorService可以帮助实现这一目标。 8. **事件处理**:Java的事件监听机制使得播放器能够响应用户的操作,如点击播放按钮或调整音量滑块。 9. **资源管理**:播放器需要有效地管理内存和系统...

    java程序设计课件

    这个“java程序设计课件”为那些已经有一定Java基础的学习者提供了深入学习和巩固知识的机会。课件中不仅涵盖了Java的基础知识,还涉及了如何实现数据库连接,这是在实际项目开发中非常实用的一项技能。 首先,Java...

    java案例经典下载

    Java的EventListener接口和相关类可以帮助我们实现这一点。 5. **算法**:游戏规则的实现需要算法支持,比如洗牌算法、判断胜败的逻辑等。 6. **IO流**:如果游戏有保存和加载功能,那么文件输入输出流...

    JAVA解惑.pdf

    《JAVA解惑》这本书主要针对...以上是《JAVA解惑》一书中可能涉及的部分知识点,每一点都值得深入学习和实践。通过掌握这些内容,开发者可以更好地解决Java编程中的各种问题,提升编程技能,写出更高效、更可靠的代码。

    java学习笔记来自java黑马B站网课,没有图片 看不了一点

    本资源“java学习笔记”源自B站上的黑马程序员Java教学课程,虽然没有图片辅助,但通过文本资料,我们可以深入理解Java的核心概念和关键知识点。 首先,Java的学习通常从基础语法开始,包括变量、数据类型、运算符...

    Java程序设计习题

    - **知识点**: 在Java中,通过继承`Thread`类来创建线程的方式之一。 - **选项分析**: - A、`classA4 extends Thread`: 正确的继承方式,但缺少`run()`方法实现。 - B、`classA4 implements Thread`: 错误,因为`...

    java课程设计音乐时钟

    Java的Thread类和Runnable接口可以帮助你实现这一点。同时,线程同步也是需要注意的问题,以避免多个线程间的冲突。 6. **资源管理**:音乐文件和其他资源的加载和释放是资源管理的一部分。理解如何正确地打开、...

    Java初学者可尝试的Java小游戏(贪吃蛇+飞机大战)

    总的来说,通过分析和实现这两个Java小游戏,初学者可以巩固理论知识,提升编程技能,并逐渐培养解决实际问题的能力。这是一个很好的学习路径,不仅能激发学习兴趣,也有助于理解Java编程的精髓。希望这个资源对你的...

    JAVA贪吃蛇源代码

    在Java中,我们可以使用`while(true)`循环来实现这一点,并通过`Thread.sleep()`方法控制游戏的帧率,以确保游戏流畅运行。 接下来,我们来看看图形用户界面(GUI)的构建。Java提供了`java.awt`和`javax.swing`包...

Global site tag (gtag.js) - Google Analytics