java进程间通信:

来源:互联网 发布:网络泄露个人隐私 编辑:程序博客网 时间:2024/05/22 03:18
java进程间通信:
(一)通信的方法:
线程之间需要一些协调通信,来共同完成一件任务。
Object类中相关的方法有两个notify方法和三个wait方法。
(wait和notify方法定义在Object类中,因此会被所有的类所继承。
  这些方法都是final的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。)


(二)wait方法:
当前的线程必须拥有当前对象的monitor,也即lock,就是锁。
wait()方法,释放锁,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法)。


确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。


2)区分wait与sleep:
Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。


(2)notify():唤醒一个等待当前对象的锁的线程。


被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。notify方法调用必须放在synchronized方法或synchronized块中,因为notify()方法应该是被拥有对象的锁的线程所调用。。


(3)总结:
1)wait()和notify()方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或synchronized块中。


2)一个线程变为一个对象的锁的拥有者是通过下列三种方法:
1.执行这个对象的synchronized实例方法。


2.执行这个对象的synchronized语句块。这个语句块锁的是这个对象。


3.对于Class类的对象,执行那个类的synchronized、static方法。




(三)范例:
(1)问题:
利用两个线程,对一个整形成员变量进行变化,一个对其增加,一个对其减少,利用线程间的通信,实现该整形变量0101这样交替的变更。


(2)方法一:


public class NumberHolder
{
    private int number;


    public synchronized void increase()//打印1
    {
        if (0 != number)
        {
            try
            {
                wait();//如果1已经打印了,则打印1的进程等待。应该打印0的进程。
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }


        // 能执行到这里说明已经被唤醒
        // 并且number为0
        number++;
        System.out.println(number);


        
        notify();//1打印完了之后,通知打印0的进程。
    }


    public synchronized void decrease()//打印0
    {
        if (0 == number)
        {
            try
            {
                wait();//如果前一个打印的是0,则打印0的等待。
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }


        }


        // 能执行到这里说明已经被唤醒
        // 并且number不为0
        number--;
        System.out.println(number);
        notify();//通知打印1的。
    }


}






public class IncreaseThread extends Thread
{
    private NumberHolder numberHolder;


    public IncreaseThread(NumberHolder numberHolder)
    {
        this.numberHolder = numberHolder;
    }


    @Override
    public void run()
    {
        for (int i = 0; i < 20; ++i)
        {
            // 进行一定的延时
            try
            {
                Thread.sleep((long) Math.random() * 1000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }


            // 进行增加操作
            numberHolder.increase();
        }
    }


}






public class DecreaseThread extends Thread
{
    private NumberHolder numberHolder;


    public DecreaseThread(NumberHolder numberHolder)
    {
        this.numberHolder = numberHolder;
    }


    @Override
    public void run()
    {
        for (int i = 0; i < 20; ++i)
        {
            // 进行一定的延时
            try
            {
                Thread.sleep((long) Math.random() * 1000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }


            // 进行减少操作
            numberHolder.decrease();
        }
    }


}






public class NumberTest
{
    public static void main(String[] args)
    {
        NumberHolder numberHolder = new NumberHolder();
        
        Thread t1 = new IncreaseThread(numberHolder);
        Thread t2 = new DecreaseThread(numberHolder);
            //打印1和打印0是对同一个对象资源的竞争,实现了同步。    
        t1.start();
        t2.start();
    }


}


-------------
(3)扩展:
如果再多加上两个线程呢?
即把其中的NumberTest类改为如下:
public class NumberTest
{
    public static void main(String[] args)
    {
        NumberHolder numberHolder = new NumberHolder();
        
        Thread t1 = new IncreaseThread(numberHolder);
        Thread t2 = new DecreaseThread(numberHolder);
        
        Thread t3 = new IncreaseThread(numberHolder);
        Thread t4 = new DecreaseThread(numberHolder);
                
        t1.start();
        t2.start();
        
        t3.start();
        t4.start();
    }


}


结果错误;
分析:
1)因为线程在wait()的时候,接收到其他线程的通知,即往下执行,不再进行判断。
2)两个线程的情况下,唤醒的肯定是另一个线程。
3)但是在多个线程的情况下,执行结果就会混乱无序。比如,一个可能的情况是,一个增加线程执行的时候,其他三个线程都在wait,这时候第一个线程调用了notify()方法,其他线程都将被唤醒,然后执行各自的增加或减少方法。


4)解决方法:
在被唤醒之后仍然进行条件判断,去检查要改的数字是否满足条件,如果不满足条件就继续睡眠。把两个方法中的if改为while即可。


public class NumberHolder
{
    private int number;


    public synchronized void increase()
    {
        while (0 != number)
        {
            try
            {
                wait();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }


        // 能执行到这里说明已经被唤醒
        // 并且number为0
        number++;
        System.out.println(number);


        // 通知在等待的线程
        notify();
    }


    public synchronized void decrease()
    {
        while (0 == number)
        {
            try
            {
                wait();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }


        }


        // 能执行到这里说明已经被唤醒
        // 并且number不为0
        number--;
        System.out.println(number);
        notify();
    }


}

0 0
原创粉丝点击