sleep()、wait()、yield()、join()方法 解析

来源:互联网 发布:到底什么是云计算 编辑:程序博客网 时间:2024/05/03 09:34

Thread 类的常用函数及功能:

 

1  sleep()   使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行, 同时sleep函数不会释放锁资源.

              sleep可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会

 

2  yield()  只是使当前线程重新回到可执行状态,所以执行yield()线程有可能在进入到可执行状态后马上又被执行. 只能使同优先级的线程有执行的机会。同样, yield()也不会释放锁资源.

 

      sleep和yield的区别在于, sleep可以使优先级低的线程得到执行的机会,  而yield只能使同优先级的线程有执行的机会.

代码示例:

            public class ThreadYieldOne implements Runnable {
     public String name;
     public void run() {
        for (int i = 0; i < 10; i++) {
           Thread.yield();    // (2)  Thread.sleep(100);
           System.out.println(name + " :" + i);
        }
     }
  }

  public static void main(String[] args) {
      ThreadYieldOne one = new ThreadYieldOne();
      ThreadYieldOne two = new ThreadYieldOne();
      one.name = "one";
      two.name = "two";
      Thread t1 = new Thread(one);
      Thread t2 = new Thread(two);
      t1.setPriority(Thread.MAX_PRIORITY);
      t2.setPriority(Thread.MIN_PRIORITY);   // (1) t2.setPriority(Thread.MAX_PRIORITY);   
      t1.start();
      t2.start();
   }

  代码执行结果:  
one :0 one :1 one :2 one :3 one :4 one :5 one :6 one :7 one :8 one :9
two :0 two :1 two :2 two :3 two :4 two :5 two :6 two :7 two :8 two :9

        注: (1) 处 如果放开注释掉t2.setPriority(Thread.MIN_PRIORITY);   , 则执行结果将会改变: 如下:  one和two 交替打印.

one :0 one :1 one :2 one :two :0 two :1 two :2 two :3 one :4 one :5 one :3 two :4 two :5 two :6 two :6 one :7 one :8 one :9
7 two :8 two :9

   (2)处 如果放开并洲释掉Thread.yield(); , 则扫行结果也是one 和two 交替打印, 并且, 它不受(1)处的影响.

 

 

3   stop()  些方法可以中止一个正在运行的线程, 但这样的方法并不安全.   强列建议不要使用此函数来中断线程.

           注:  stop方法是立即停止当前线程,  这样停止的后果是导致stop后的语句无法执行, 有可能资源未释放. 列或者在同步块中调用此方法会导致同步数据会不完整. 所以需禁用此方法.  由于stop方法的特释性, 将不给出示范代码.

 

 

4   interrupt()  一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序.

                  Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态,

                 两个解释为什么会相冲突呢, 一个解释是可以中断一个线程. 一个解释说不会中断一个正在运行的线程.  仔细看会发现其中的奥秒. interrupt方法不会中断一个正在运行的线程.就是指线程如果正在运行的过程中, 去调用此方法是没有任何反应的. 为什么呢, 因为这个方法只是提供给 被阻塞的线程, 即当线程调用了.Object.wait, Thread.join, Thread.sleep三种方法之一的时候, 再调用interrupt方法, 才可以中断刚才的阻塞而继续去执行线程.

             代码说明一切.

            情况1.  不会中断线程.

                            public class guoInterrupt extends Thread {
           public boolean stop = false;
         public static void main(String[] args) throws InterruptedException {
            guoInterrupt t1 = new guoInterrupt();
            System.out.println("app is starting");
            t1.start();
            Thread.sleep(3000);
            System.out.println("Interrupting t1....");
            t1.interrupt();
            Thread.sleep(3000);
            System.out.println("app is end");
            t1.stop = true;
            System.exit(0);
         }
         public void run() {
            while(!this.stop) {
               System.out.println("t1 running...........");
             l  ong time = System.currentTimeMillis();
               while ( (System.currentTimeMillis()-time < 1000) && (!stop) ) {};
            }
            System.out.println("t1 is end");
         }
      }

      执行结果:

app is starting
t1 running...........
t1 running...........
t1 running...........
t1 running...........
Interrupting t1....
t1 running...........
t1 running...........
t1 running...........
app is end
t1 is end

 

结果说明. 当调用了interrupt时, t1线程仍在执行, 并没有中断线程. 直到main扫行结束后, 改t1的stop值时 t1线程才执行结束.

 

    情况2:  interrupt中断线程.

                             public class guoInterrupt extends Thread {
           public boolean stop = false;
           public static void main(String[] args) throws InterruptedException {
              guoInterrupt t1 = new guoInterrupt();
              System.out.println("app is starting");
              t1.start();
              Thread.sleep(3000);
              System.out.println("Interrupting t1....");
              t1.stop = true;
              t1.interrupt();
              Thread.sleep(3000);
              System.out.println("app is end");
              System.exit(0);
           }
           public void run() {
              while(!this.stop) {
                 System.out.println("t1 running...........");
                 try {
                    Thread.sleep(1000);
                 } catch (InterruptedException e) {
                    System.out.println("t1 is Interrupting......");
                 }
              }
              System.out.println("t1 is end");
           }
        }  

      执行结果:

app is starting
t1 running...........
t1 running...........
t1 running...........
Interrupting t1....
t1 is Interrupting......
t1 is end
app is end

 

结果说明: 当执行了 t1.interrupt();方法时, 线程立即产生了一个.InterruptedException 异常.

 

 

 

5   join()  当join(0)时等待一个线程直到它死亡,  当join(1000)时等待一个线程1000纳秒,后回到主线程继续执行.

         代码示例:

    public static void main(String[] args) {
        ThreadJoin t = new ThreadJoin();
        try {
            t.start();
            Thread.sleep(1000);
            System.out.println("main join start");
            t.join(0);  // (1)
            System.out.println("main join end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }             

 public class ThreadJoin extends Thread {
    public void run() {
            System.out.println("join start");
            try {
                Thread.sleep(9000);
                for (int i = 0; i < 5; i++) {
                    System.out.println("sub thread:" + i);
                }
            } catch (InterruptedException e) {
            }
            System.out.println("join end");
    }
}

 说明: (1) 为t.join(0)时 等待t线程执行完之后回到main函数的主线程继续处理.

执行结果

join start
main join start
sub thread:0
sub thread:1
sub thread:2
sub thread:3
sub thread:4
join end
main join end

 

             (1) 处改为t.join(1000)时, main函数的主线程等待t经程1000纳秒后继续执行

   执行结果:

join start
main join start
main join end
sub thread:0
sub thread:1
sub thread:2
sub thread:3
sub thread:4
join end

注:  join函数为线程安全函数, 即同步函数. 也就是说上面的例子, 当ThreadJoin类的run用synchronized锁住时, t.join方法将得不到锁资源而等待更长的时间.

代码示例:

public class guoJoin {
    public static void main(String[] args) {
        ThreadJoin t = new ThreadJoin();
        try {
            t.start();
            Thread.sleep(1000);
            System.out.println("main join start");
            t.join(1000);
            System.out.println("main join end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadJoin extends Thread {
    public void run() {
        synchronized (this) {
            System.out.println("join start");
            try {
                Thread.sleep(9000);
                for (int i = 0; i < 5; i++) {
                    System.out.println("sub thread:" + i);
                }
            } catch (InterruptedException e) {
            }
            System.out.println("join end");
        }
    }
}

执行结果

join start
main join start
sub thread:0
sub thread:1
sub thread:2
sub thread:3
sub thread:4
join end
main join end

结果说明: 当main函数中调用t.join();时, 由于 t.run()方法对t线程做了同步处理, 即得到了锁资源, 而此时t.join()方法调用时只能等待t.run()方法执行完成之后, 释放了锁资源之后. t.join函数才可以继续等待. 即main实际等待了 9000 + 1000纳秒.

 

 

 

6   suspend(), resume() 这两个是JDK的过期方法. suspend()函数,可使线程进入停滞状态.  通过suspend()使线程进入停滞状态后,除非收到resume()消息,否则该线程不会变回可执行状态

示范代码

public class guoSuspend {
    public static void main(String[] args) {
        ThreadSuspend t = new ThreadSuspend();
        t.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
        }
        System.out.println("main suspend start");
        t.suspend();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        t.resume();
        System.out.println("main resume end ");
    }
}

public class ThreadSuspend extends Thread {
    public void run() {
        for (int i = 0; i < 6; i++) {
            System.out.println("ThreadSuspend:" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
    }
}

执行结果:

ThreadSuspend:0
main suspend start
main resume end 
ThreadSuspend:1
ThreadSuspend:2
ThreadSuspend:3
ThreadSuspend:4
ThreadSuspend:5

 

三  多线程的同步

 

synchronized, wait, notify

线程的同步需要依靠上面两个函数和一个同步块实现.

     同步, 即两个线程为了一件事情协同合作执行. 例如, 缓冲区的读写操作. 在实际应用中, 例如:/

           有一个缓冲区, 由两个线程进行操作, 一个写线程, 一个读线程. 写线程完成对缓冲区的写入, 读线程完成对线程的读取, 只有当缓冲区写入数据时, 读线程才可以读取, 同样. 只有当缘冲区的数据读出时,  写线程才可以向缘冲区写入数据.

代码示例:

public class ThreadWrite extends Thread {
    public StringBuffer buffer;
    public ThreadWrite(StringBuffer buffer) {
        this.buffer = buffer;
    }
    public void run() {
        synchronized (this.buffer) {
            for (int i = 0; i < 5; i++) {
                if (!"".equals(this.buffer.toString())) {
                    try {
                        buffer.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } 
                System.out.println("Write start");
                this.buffer.append("123");
                this.buffer.notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
                System.out.println("Write end");
            }
        }
    }
}

 

public class ThreadRead extends Thread {
    public StringBuffer buffer;
    public ThreadRead(StringBuffer buffer) {
        this.buffer = buffer;
    }
    public void run() {
        synchronized (buffer) {
            for (int i = 0; i < 5; i++) {
                if ("".equals(this.buffer.toString().trim())) {
                    try {
                        buffer.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("read start");
                System.out.println(this.buffer.toString());
                buffer.delete(0, buffer.toString().length());
                buffer.notify();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
                System.out.println("read end");
            }
        }
    }
}

public class GuoSynchronized {
    public static void main(String[] args) {
        StringBuffer bufer = new StringBuffer("");
        ThreadWrite write = new ThreadWrite(bufer);
        ThreadRead read = new ThreadRead(bufer);
        read.start();
        write.start();
    }
}

执行结果:

Write start
Write end
read start
123
read end
Write start
Write end
read start
123
read end
Write start
Write end
read start
123
read end
Write start
Write end
read start
123
read end
Write start
Write end
read start
123
read end

结果说明: 这个结果不管执行多少次, 都会是相同的结果, 即多线程同步的特点.

注: synchronized  关键字的同步, 在两个线程之间必须是同一个对象, 即 对同一个对象进行同步访问.

 

四  死锁

 

为什么会产生死锁? 在多线程环境里, 产生死锁的原因是由于几个线程同时共用一个同步资源. 这是产生死锁的前提,

产生死锁的原因, 所有线程都在等待共享资源的释放.

例如:

线程1写缓冲区时, 缓冲区已满 , 线程1等待. 

线程2读缓冲区, 当缓冲区读完时, 唤醒共享资源锁上的1个线程, 并且线程2再去读取缓冲区时, 由于缓冲区被读完, 线程2等持.

刚刚线程2唤醒了一个共享资源锁上的1个线程, 而共享资源锁上的线程可能有好几个, 有可能是读线程, 有可能是写线程. 假如此时唤醒的正好是读线程3, 那么线程3去读取缓冲区, 这时的缓冲区已经线程2读完了, 线程3需要等持缓冲区被写入数据时才可以读取缓冲区, 即此时线程3也被阻塞了. 即等待.

此时, 线程1等待,    线程2由于读完了, 也等待, 线程3的情况和2一样, 也等待. 其它共享资源上的线程也都在等待, 死锁即发生了.

 

看代码示例:

public class GuoSynchronized {
    public static void main(String[] args) {
        StringBuffer bufer = new StringBuffer("");
        ThreadWrite write1 = new ThreadWrite(bufer,"w1");
        ThreadWrite write2 = new ThreadWrite(bufer,"w2");
        ThreadWrite write3 = new ThreadWrite(bufer,"w3");
        ThreadRead read1 = new ThreadRead(bufer,"r1");
        ThreadRead read2 = new ThreadRead(bufer,"r2");
        ThreadRead read3 = new ThreadRead(bufer,"r3");
        read1.start();
        read2.start();
        read3.start();
        write1.start();
        write2.start();
        write3.start();
    }
}

public class ThreadRead extends Thread {
    public StringBuffer buffer;
    public ThreadRead(StringBuffer buffer, String name) {
        super(name);
        this.buffer = buffer;
    }
    public void run() {
        synchronized (buffer) {
            for (int i = 0; i < 5; i++) {
                while ("".equals(this.buffer.toString().trim())) {
                    try {
                        buffer.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("read " + Thread.currentThread().getName() + " start");
                System.out.println(this.buffer.toString());
                buffer.delete(0, buffer.toString().length());
                buffer.notify();
                System.out.println("read " + Thread.currentThread().getName() + " end");
            }
        }
    }
}

public class ThreadWrite extends Thread {
    public StringBuffer buffer;
    public ThreadWrite(StringBuffer buffer, String name) {
        super(name);
        this.buffer = buffer;
    }
    public void run() {
        synchronized (this.buffer) {
            for (int i = 0; i < 5; i++) {
                while (!"".equals(this.buffer.toString())) {
                    try {
                        buffer.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } 
                System.out.println("Write " + Thread.currentThread().getName() + " start");
                this.buffer.append("123");
                System.out.println("123");
                this.buffer.notify();
                System.out.println("Write " + Thread.currentThread().getName() + " end");
            }
        }
    }
}

 

执行结果很有可能被死锁住了.

 要改变这一状况, 可将, 两个线程类的this.buffer.notify();函数改为this.buffer.notifyAll();即可解除这种死锁.

 

上面由于存在代码示例,显得有些冗长,下面简单总结下:

 

     1.sleep()方法
  在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。
  sleep()使当前线程进入阻塞状态,在指定时间内不会执行。
  2.wait()方法
  在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。
  当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。
  唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。
  waite()和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通       过,但在运行时会发生IllegalMonitorStateException的异常。
  3.yield方法
  暂停当前正在执行的线程对象。
  yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
  yield()只能使同优先级或更高优先级的线程有执行的机会。
  4.join方法
  等待该线程终止。
  等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。

比较重要的几点需要注意:
1 sleep和yield的区别在于, sleep可以使优先级低的线程得到执行的机会,  而yield只能使同优先级的线程有执行的机会.
2 interrupt方法不会中断一个正在运行的线程.就是指线程如果正在运行的过程中, 去调用此方法是没有任何反应的. 为什么呢, 因为这个方法只是提供给 被阻塞的线程, 即当线程调用了.Object.wait, Thread.join, Thread.sleep三种方法之一的时候, 再调用interrupt方法, 才可以中断刚才的阻塞而继续去执行线程.
3 join()  当join(0)时等待一个线程执行直到它死亡返回主线程,  当join(1000)时主线程等待一个线程1000纳秒,后回到主线程继续执行.
4 waite()和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

 

原创粉丝点击