首页 > 代码库 > Java并发编程:锁的释放

Java并发编程:锁的释放

<style type="text/css">.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd { color: #5f9ea0 } .right { margin-left: auto; margin-right: 0px; text-align: right } .left { margin-left: 0px; margin-right: auto; text-align: left } .center { margin-left: auto; margin-right: auto; text-align: center } .underline { text-decoration: underline } #postamble p,#preamble p { font-size: 90%; margin: .2em } p.verse { margin-left: 3% } pre { border: 1px solid #ccc; padding: 8pt; font-family: monospace; overflow: auto; margin: 1.2em } pre.src { position: relative; overflow: visible; padding-top: 1.2em } pre.src::before { display: none; position: absolute; background-color: white; top: -10px; right: 10px; padding: 3px; border: 1px solid black } pre.src:hover::before { display: inline } pre.src-sh::before { content: "sh" } pre.src-bash::before { content: "sh" } pre.src-emacs-lisp::before { content: "Emacs Lisp" } pre.src-R::before { content: "R" } pre.src-perl::before { content: "Perl" } pre.src-java::before { content: "Java" } pre.src-sql::before { content: "SQL" } table { border-collapse: collapse } caption.t-above { caption-side: top } caption.t-bottom { caption-side: bottom } td,th { vertical-align: top } th.right { text-align: center } th.left { text-align: center } th.center { text-align: center } td.right { text-align: right } td.left { text-align: left } td.center { text-align: center } dt { font-weight: bold } .footpara:nth-child(0n+2) { display: inline } .footpara { display: block } .footdef { margin-bottom: 1em } .figure { padding: 1em } .figure p { text-align: center } .inlinetask { padding: 10px; border: 2px solid gray; margin: 10px; background: #ffffcc } #org-div-home-and-up { text-align: right; font-size: 70%; white-space: nowrap } textarea { } .linenr { font-size: smaller } .code-highlighted { background-color: #ffff00 } .org-info-js_info-navigation { border-style: none } #org-info-js_console-label { font-size: 10px; font-weight: bold; white-space: nowrap } .org-info-js_search-highlight { background-color: #ffff00; color: #000000; font-weight: bold }</style> <style type="text/css">code { color: #FF0000 } pre.src { background-color: #002b36; color: #839496 }</style>

Java并发编程:锁的释放

Table of Contents

  • 1. 线程的状态
  • 2. wait() notify() 和 notifyAll()

上一篇线程的同步,我们讲了锁的获得方式。接下来,我们讲讲锁的释放。首先,锁定的方法或者代码块运行完毕,肯定会释放锁。
那么,主动释放锁,是通过什么方法来达到的呢?
如果,我们看过Object类的方法,我们会注意到它有三个奇怪的方法:wait(),notify()和notifyAll()。 它们就是用来释放锁的。

1 线程的状态

在讲锁的释放前,我们先讲一下线程的三种主要状态:运行、就绪(可运行)、阻塞。当然,除了这个之外,肯定还有初始状态和结束状态,那个就不讨论了。

技术分享

当我们创建线程之后,还只是进入初始状态,如果我们调用run()方法运行,根本就不会启动新的线程。当调用start()后,可以将线程状态改为可运行状态,然后,由操作系统来决定调用哪个线程。当幸运的被操作系统选中之后,就可以进入真正的运行状态了。当运行当时间片用完,或者调用yield()礼让方法,就会把当前当执行权交出来,进入可运行就绪状态。如果在运行的过程中,有系统IO,如等待输入,弹出确认对话框等,则会使当前线程进入阻塞状态,动弹不得。只有等待用户操作之后,才能往下进行。sleep()方法和join()方法可以可以达到类似的阻塞效果。

技术分享
然后,我们把对象锁部分的状态也加进来。当我们使用synchronized修饰方法或者代码块时,会获取对象的锁,并进入获取该对象锁的等待队列。当获取到该对象锁之后,就可以进入可运行状态了。
另外,还有一个对象的wait()方法,可以使线程放弃持有的该对象锁,并进入通知等待状态。当其他线程调用等待线程需要的对象的notify()或者notifyAll()方法时,该线程重新进入获取对象锁的队列中参与锁的获取。

2 wait() notify() 和 notifyAll()

synchronized获取锁的方法我们已经详细的讲解过了,我们接下来来看一下如何主动释放锁。假设,我们想创建两个线程,让这两个线程,依次做一件事情,而不是同时启动之后,就由操作系统来决定它们的执行顺序,那么,我们该怎么做呢?
那就是首先请求前一个线程的锁,然后,获取自己线程的锁,再释放自己的锁,并通知这个锁对象的等待队列(下一个线程!哎哎,醒醒别睡啦,起来干活啦!),释放前一个线程的锁,并进入等待前一个锁对象的通知队列。

技术分享

用代码展示为:

public class SyncDemo implements Runnable {
    private String name;
    private Object prev;
    private Object cur;

    public SyncDemo(String name, Object prev, Object cur) {
        this.name = name;
        this.prev = prev;
        this.cur = cur;
    }

    @Override
    public void run() {
        int count = 10;
        while (count > 0) {
            synchronized(prev) {
                synchronized(cur) {
                    System.out.println(name);
                    count--;
                    cur.notify();
                }
                try {
                    prev.wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        Object a = new Object();
        Object b = new Object();
        SyncDemo r1 = new SyncDemo("A", b, a);
        SyncDemo r2 = new SyncDemo("B", a, b);

        new Thread(r1).start();
        try {
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(r2).start();
    }
}

写得更通用一点,支持更多的线程依次执行:

public static void main(String[] args) {
    int num = 5;

    Object[] locks = new Object[num];
    String[] names = {"A", "B", "C", "D", "E"};
    SyncDemo[] runnable = new SyncDemo[num];

    for (int i = 0; i < num; i++) {
        locks[i] = new Object();
    }

    for (int i = 0; i < num; i++) {
        runnable[i] = new SyncDemo(names[i], locks[(i - 1 + num) % num] , locks[i]);
    }

    for (int i = 0; i < num; i++) {
        new Thread(runnable[i]).start();
        try {
            Thread.sleep(100);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Date: 2017-07-05 22:31

Author: WEN YANG

Created: 2017-07-06 Thu 23:14

Emacs 25.2.1 (Org mode 8.2.10)

Validate

Java并发编程:锁的释放