Java线程同步那些事

Posted warmor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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线程的同步就讲到这里了,应该算是比较全面了,欢迎大家留言讨论~

以上是关于Java线程同步那些事的主要内容,如果未能解决你的问题,请参考以下文章

python并发线程那些事

多线程的那些事-03

java多线程

Java 并发编程解析 | 关于线程机制的那些事,你究竟了解多少?

线程池那些事之ThreadPoolExecutor

Java线程池的那些事