多线程

来源:互联网 发布:淘宝客服昵称大全修改 编辑:程序博客网 时间:2024/06/05 09:41

Wait():使持有对象锁的线程释放锁

Wait(long):使持有对象锁的线程释放锁,时间为long毫秒后,再次获得锁wait()wait(0)等价

Notify():唤醒一个正在等待该对象锁的线程,如果等待的线程不止一个,那么被唤醒的线程由jvm确定

NotifyAll():唤醒所有正在等待该对象锁的线程

 

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),学过操作系统的同学对它一定已经很熟悉了。Java 提供了大量方法来支持阻塞,下面让我们逐一分析。

1.     sleep() 方法:

sleep() 允许指定以毫秒为单位的一段时间作为参数,它使得线程在指定的时间内进入阻塞状态,不能得到CPU 时间,指定的时间一过,线程重新进入可执行状态。

典型地,sleep() 被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。

2.     suspend() resume() 方法:

两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume() 被调用,才能使得线程重新进入可执行状态。

典型地,suspend() resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume() 使其恢复。

3. yield() 方法:

yield() 使得线程放弃当前分得的 CPU 时间,但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另一个线程。

4.    wait() notify() 方法:

两个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种允许指定以毫秒为单位的一段时间作为参数,另一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程重新进入可执行状态,后者则必须对应的 notify() 被调用。

初看起来它们与 suspend() resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的所有方法,阻塞时都不会释放占用的锁(如果占用了的话),而这一对方法则相反。上述的核心区别导致了一系列的细节上的区别。

  首先,前面叙述的所有方法都隶属于 Thread 类,但是这一对却直接隶属于 Object 类,也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用 任意对象的notify()方法则导致因调用该对象的 wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,前面叙述的所有方法都可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

wait() notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 block wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。

关于 wait() notify() 方法最后再说明两点:

第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态。

谈到阻塞,就不能不谈一谈死锁,略一分析就能发现,suspend() 方法和不指定超时期限的 wait() 方法的调用都可能产生死锁。遗憾的是,Java 并不在语言级别上支持死锁的避免,我们在编程中必须小心地避免死锁。

以上我们对 Java 中实现线程阻塞的各种方法作了一番分析,我们重点分析了 wait() notify() 方法,因为它们的功能最强大,使用也最灵活,但是这也导致了它们的效率较低,较容易出错。实际使用中我们应该灵活使用各种方法,以便更好地达到我们的目的。

 

补充:

1.    sleep()yield()区别

    当线程运行中执行了Thread类的yield()静态方法,如果此时具有相同优先级的线程处于就绪状态,那么yield方法将把当前运行的线程放到可运行池中并使另一个线程运行。如没有则yield方法什么都不做。而sleep方法也会给低优先级线程一个运行的机会。

2.    join():调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行。

Join示例:

 

package org;

public class TestRunnable{

 

        public static void main(String[] args ){

        Runner2 r=new Runner2();

        Thread t1=new Thread(r); //Runner2是实现了Runnable接口的类

        Thread t2=new Thread(r);

        t1.start();

        try{

            t1.join();

        }catch(Exception e){

            e.printStackTrace();

        }

        System.out.println("main go on--->");

        t2.start();

    }

   

}

class Runner2 implements Runnable{

   

       public void run(){

 

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

           String  s=Thread.currentThread().getName();

           if(s.compareTo("Thread-1")==0)

              System.out.print("                    ");

           System.out.println("i="+i+"s="+s);

           }

       }

}

 

 

结果一:注释掉t1.join();

main go on--->

i=0s=Thread-0

i=1s=Thread-0

i=2s=Thread-0

i=3s=Thread-0

i=4s=Thread-0

i=5s=Thread-0

i=6s=Thread-0

i=7s=Thread-0

i=8s=Thread-0

i=9s=Thread-0

                    i=0s=Thread-1

                    i=1s=Thread-1

                    i=2s=Thread-1

                    i=3s=Thread-1

                    i=4s=Thread-1

                    i=5s=Thread-1

                    i=6s=Thread-1

                    i=7s=Thread-1

                    i=8s=Thread-1

                    i=9s=Thread-1

结果二:加上t1.join();

i=0s=Thread-0

i=1s=Thread-0

i=2s=Thread-0

i=3s=Thread-0

i=4s=Thread-0

i=5s=Thread-0

i=6s=Thread-0

i=7s=Thread-0

i=8s=Thread-0

i=9s=Thread-0

main go on--->

                    i=0s=Thread-1

                    i=1s=Thread-1

                    i=2s=Thread-1

                    i=3s=Thread-1

                    i=4s=Thread-1

                    i=5s=Thread-1

                    i=6s=Thread-1

                    i=7s=Thread-1

                    i=8s=Thread-1

                    i=9s=Thread-1

说明:结果二在理想的情况下应该是Thread-1Thread-0是交替进行的,在这里循环了10次,所以结果不明显,如果循环1000次,结果就明显了。

 

 

原创粉丝点击