初识java多线程的wait(), notify()方法

初识java多线程的wait(), notify()方法

    最近需要数据,研究了爬虫。后来发现单线程爬虫的速度不快,又研究了一下多线程。啊啊啊啊!我的文本情感分析之路什么时候才能开始呀。QAQ


本文参考了 :http://www.cnblogs.com/lwbqqyumidi/p/3817517.html

1,简介

    在多线程编程的过程中,往往会涉及到线程间的通信以及公共资源的使用。为此java提供了若干方法。wait()和notify()用于进程间的通信,synchronized用于共享资源的使用权限。
    本文将通过两个线程分别输出单数和双数的例子介绍wait()和 notify()方法。

2,wait()和notify()

    wait()和notify()函数在上述参考博客的定义如下:
这里写图片描述
开始看不明白notify()唤醒同步锁上的其他等待线程是什么意思。看了博主给的例子也不太明白。所以自己就写了一个输出单双数的程序帮助理解。程序主要是开两个线程,共享一个变量i,当i是单数的时候,由线程A输出i的值,当i是双数的时候用线程B输出i的值。共享变量i放在一个count的class中。代码如下:

class count {
    private int i;
    //是否为单数
    private boolean flag = true;
    public count(int i) {
        this.i = i;
    }
    //打印单数
    public synchronized void print_single() throws InterruptedException {
        for(int j = 0; j < 10; j++) {
            if(flag) {
                System.out.println(Thread.currentThread().getName() + "---" + i + ",单数," + j);
                i++;
                flag = false;
                notify();
            } else {
                wait();
            }
        }
    }

    //打印双数
    public synchronized void print_double() throws InterruptedException {
        for(int j = 0; j < 10; j++) {
            if(flag) {
                wait();
            } else {
                System.out.println(Thread.currentThread().getName() + "---" + i + ",双数," + j);
                i++;
                flag = true;
                notify();
            }
        }
    }
}

类中还包含打印单数和打印双数的函数。在使用wait()和notify()函数的时候,一定要将这两个函数放在synchronized中。count类中还有个标志位flag,用于判断i是否为单数。
同时写了两个实现Runnable的线程类用于调用count类中的输出单双数的函数。代码如下:

//创建一个新线程用于调用count类中的输出单数的函数
class singleRunnable implements Runnable {
    private count c;

    public singleRunnable(count c) {
        this.c = c;
    }

    public void run() {
        try {
            c.print_single();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
//创建一个新线程用于调用count类中的输出双数的函数
class doubleRunnable implements Runnable {
    private count c;

    public doubleRunnable(count c) {
        this.c = c;
    }

    public void run() {
        try {
            c.print_double();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

两个线程类中各有一个count的对象,在主程序中将会把同一个count对象传给这两个线程类作为两个线程类的公共资源。
最后是主函数。用于创建一个count对象和两个线程类并启动这两个线程。

public static void main(String[] args) throws IOException {
        count c = new count(1); //初始化count 设置i=1
        Runnable single = new singleRunnable(c);//创建一个线程类将count对象传入
        Runnable Double = new doubleRunnable(c);//创建一个线程类将count对象传入
        Thread tmp = new Thread(single);
        Thread tmp1 = new Thread(Double);
        tmp.start();//开始线程
        tmp1.start();//开始线程
    }

运行程序,结果如下:
这里写图片描述
咋看没有什么问题,线程1和线程2交替输出i的单双数。但是仔细看下发现程序一直没有结束还在运行,并且打印的j的值都为偶数。这就不对劲了。于是博主改变了count类中j的值,将j改成了奇数5。运行结果如下:
这里写图片描述
程序顺利执行完毕。后来又将j改成了3同样可以执行完毕。最后博主又将j改成几个偶数,结果跟第一次一样程序始终在执行。于是博主将j改成2跟随程序走了一遍发现了问题。
当两个线程启动时,i的初始值为1,flag=true。由于主函数中打印单数的线程在打印双数的线程前面进入准备状态,所以先执行print_single()函数。在print_single()中,输出i为1之后,i自增1变成2。将flag改为false,同时唤醒另一个打印双数的进程。但由于调用notify()函数后,被唤醒的线程不会马上执行,要等唤醒它的程序执行完或调用wait()后才会执行。所以print_single()函数在唤醒print_double()函数后还将继续执行,此时j自加变成1,然再判断flag,现在flag为false,所以调用wait()挂起。这时print_double()函数就开始执行了。print_double()开始执行时,j == 0,此时i==2。所以打印出的i和j分别为2,0。然后print_double()函数唤醒另一线程,同时继续执行。此时j变成1,i变成3,打印双数的线程执行wait()函数进入等待状态。等到print_single()函数开始执行时,j的值为1,从上次wait()函数停止的地方开始执行,所以j先自增变成2,判断条件不符后,结束该线程。而此时打印双数的线程任然在等待,并且由于没有notify()函数唤醒,将一直等待下去,所以就出现了我们之前看到的程序一直执行,以及打印出来的j的值都为偶数。
当我们把j的值换成奇数时,就可以正常执行完毕。
当我们在主程序中改变两个线程的启动顺序的时候即改成

public static void main(String[] args) throws IOException {
        count c = new count(1, 0);
        Runnable single = new singleRunnable(c);
        Runnable Double = new doubleRunnable(c);
        Thread tmp = new Thread(single);
        Thread tmp1 = new Thread(Double);


        //跟前面相比,交换了打印单双数线程的启动顺序
        tmp1.start();
        tmp.start();
        //--------------------------------

    }

j的值还是偶数时,这时程序也可以顺利执行完毕。
可以根据我分析第一种的方法,分析以上的两种情况。

3,总结

    由此我们可以发现,虽说多线程给我们的感觉是同时进行的,但是程序真正执行的时候还是有顺序之分的。将两线程启动顺序改变一下就会影响输出的结果。同时我们可以看出,wait()和notify()函数在本例子中作用的是一个对象中的不同方法,借此可以理解一下“wait(),notify()函数是作用在同步锁对象的线程上”这句话的含义。
完整代码如下:

import java.io.IOException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class count {
    private int i;

    //是否为单数
    private boolean flag = true;
    public count(int i) {
        this.i = i;
    }

    //打印单数
    public synchronized void print_single() throws InterruptedException {
        for(int j = 0; j < 2; j++) {
            if(flag) {
                System.out.println(Thread.currentThread().getName() + "---" + i + ",单数," + j);
                i++;
                flag = false;
                notify();
            } else {
                wait();
            }
        }
    }

    //打印双数
    public synchronized void print_double() throws InterruptedException {
        for(int j = 0; j < 2; j++) {
            if(flag) {
                wait();
            } else {
                System.out.println(Thread.currentThread().getName() + "---" + i + ",双数," + j);
                i++;
                flag = true;
                notify();
            }
        }
    }
}

class singleRunnable implements Runnable {
    private count c;

    public singleRunnable(count c) {
        this.c = c;
    }

    public void run() {
        try {
            c.print_single();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class doubleRunnable implements Runnable {
    private count c;

    public doubleRunnable(count c) {
        this.c = c;
    }

    public void run() {
        try {
            c.print_double();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

public class RunnableTest {
    public static void main(String[] args) throws IOException {
        count c = new count(1);
        Runnable single = new singleRunnable(c);
        Runnable Double = new doubleRunnable(c);
        Thread tmp = new Thread(single);
        Thread tmp1 = new Thread(Double);
        tmp.start();
        tmp1.start();
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值