Java线程同步那些事

  讲线程同步之前先理解一个概念:monitor,即监视器,也叫管程,是用来管理线程同步的东东,可以把它理解为一个房间的钥匙,想要进入房间,使用房间里的东西,就必须先要拿到钥匙。并且同一时刻只能有一个线程可以拿到它。

  java中线程同步是用wait/notify,join来实现的,话不多说,直接上代码。

public class MyTst {
    /**
     * @param args
     */
    private volatile boolean ready = false;

    class MyThread1 implements Runnable  {

        @Override
        public void run() {
            System.out.println("t1 run...");

            try {
                Thread.sleep(2000);
                if (!ready) {
                    setReady();
                }
                System.out.println("t1 finish");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class MyThread2 implements Runnable {
        @Override
        public void run() {
            myTest();
        }
    }

    synchronized void myTest(){
    	---location 1---
        if (!ready) {
            try {
                System.out.println("wait..");
                wait();
                System.out.println("wait after, do something...");
            }catch (InterruptedException e) {
                System.out.println("oh! InterruptedException..");
            }
        }
    }

    synchronized void setReady() {
        ready = true;
        notifyAll();
    }

    public void starWork() throws InterruptedException {
        Thread thread1 = new Thread(new MyThread1());
        thread1.start();

        Thread thread2 = new Thread(new MyThread2());
        thread2.start();
        //---location 2---
        
		//---location 3---
    }

    public static void main(String[] args) throws InterruptedException{
        System.out.println("main run...");

        MyTst myTst = new MyTst();
        myTst.starWork();
    }
}

  代码中声明了一个布尔型的变量ready,一个线程负责赋值为true,并且唤醒等待的线程,另一个线程负责读取,如果读取为false就用wait一直等。
  运行程序,打印如下:
main run…
t1 run…
wait…
t1 finish
wait after, do something…
  看起来很正常,对吧?实际上是有问题的,不信我们在location 2处加上两行代码

Thread.sleep(100);
thread2.interrupt();

  打印如下:
main run…
t1 run…
wait…
oh! InterruptedException…
t1 finish
  纳尼??没有等线程1 notifyAll,结果线程2直接不等就结束了。。。这是为啥呢?原来是因为主线程在休眠 100毫秒之后,调用了interrupt(),这样线程2由于在等待状态,直接就抛出了InterruptedException异常,所以就结束了。为了避免这种情况,一般是把wait()包裹在while循环里,也就是把location 1处的if改为while,修改后运行情况如下:
main run…
t1 run…
wait…
oh! InterruptedException…
wait…
t1 finish
wait after, do something…
  这样就一切都正常啦!整个的过程实际上是这样滴:
  线程2执行到myTest()时,发现是sychrozined方法,就去拿monitor,如果被其他线程占用,就要等待,如果没有被占用,就可以拿到,然后继续执行,走到while判断时发现ready是false,就进入等待队列,并且释放monitor,等线程1 休眠2秒后执行setReady(),同样的发现是synchronized方法,就去拿monitor,如果没被占用,就可以拿到,然后继续执行,唤醒线程2,并且释放monitor,线程2被唤醒后,去尝试拿monitor,如果被占用,就排队等待,如果没有,就可以拿到,并继续执行下去,执行完myTest()后,就释放monitor。
  这里有三点需要我们注意:
1.线程被notify唤醒后,还需要排队拿monitor(如果monitor正在被占用),然后才能继续往下执行。(monitor是属于MyTst类的)
2.wait,notifyAll需要写在synchronized函数里面。
3.虽然有notify方法,但基本都用notifyAll,因为不知道有几个线程在等,也就不知道要调用几次notify,索性就用notifyAll,一劳永逸。
下面再来看看join()方法,我们在location 3处加上如下代码:

System.out.println("thread2 join me");
thread2.join();
System.out.println("finish work..");

也就是等线程2执行完之后,再执行主线程,在此之前主线程处于阻塞状态。看看打印:
main run…
t1 run…
wait…
thread2 join me
oh! InterruptedException…
wait…
t1 finish
wait after, do something…
finish all work…
  嗯!符合预期,需要说明的是执行join()时,如果线程被打断,是会抛出InterruptedException异常的,这点跟sleep(),wait()是一样的。
  线程中还有一个yield()方法,这个是把cpu的时间片段让出来,给相同优先级的其他线程以执行机会。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
  好了,关于Java线程的同步就讲到这里了,应该算是比较全面了,欢迎大家留言讨论~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值