synchronized静态代码块,以及其中的wait,notify和notifyAll

接触Java多线程这么久了,synchronized静态代码块以及其中的wait和notify方法一直没搞懂,网上关于此,不是旁敲侧击,就是晦涩难懂(我理解能力有限),实在无语了。今天把Java的源码导入到了eclipse,从源码中看到了我想要的答案。

即本文专注于讲解透彻地讲解synchronized代码块,wait以及notify的使用方法,绝对一针见血,绝不旁敲侧击,没意思。

注:本文不会讲什么是Java同步机制,以及为何要同步

我们开始吧。

一.抛出问题

1.notify和wait到底是属于线程,还是属于Object,它们的调用对线程又有着什么影响?

2.网上说notify和wait必须在synchronized代码块之内调用,为什么?

二.用Java源码解决问题

问题一:

我们来看源代码:Object的实现类

public class Object {
//这三个native方法是从Object源代码中找到的三个函数
    public final native void notify();
    public final native void notifyAll();
    public final native void wait(long timeout) throws InterruptedException;
}

1.我们知道,Java中Object是一切类的父类。看到这里,有人说应该是子类继承了父类的这几个方法吧,但是我打开了String,Thread的实现类,搜索wait或者notify这个字符串竟然没搜到!说明了什么,说明这些方法属于Object,子类直接继承,根本就没做任何更改。即无论是哪个类的wait和notify,实现的效果都是相同的,只是我们现在还不知道具体是什么效果,所以我们只要搞懂了以上三个函数,一切都真相大白了。

2.有人说,上面不是三个抽象方法吗,什么都没实现啊,额,那你就需要查查native这个关键字是什么作用了。

下面我插一段来简单介绍一下native的意思:

简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。标识符native可以与所有其它的java标识符连用,但是abstract除外。这是合理的,因为native暗示这些方法是有实现体的,只不过这些实现体是非java的,但是abstract却显然的指明这些方法无实现体。

恩,是的,这些方法都已经实现了,只是不是用Java语言实现的,那我们是不是 对其用法就不得而知了?当然不是,Object用注释的方式给出了它们的具体用法,我们一起来看一下:(我会把重要的翻译一下,不要一掠而过,这是重点哦)

//先来看看对wait方法的注释:
Causes the current thread to wait until another thread invokes the java.lang.Object.notify() method or the java.lang.Object.notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).
/**上一段话的意思是:一个Object的wait方法会导致当前线程等待,直到其
*他的线程调用同一个Object对象的notify方法或者notifyAll方法,
*换句话说,这个方法完全等同于wait(0);
*/
The current thread must own this object‘s monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object‘s monitor to wake up either through a call to the notify method or the notifyAll method.
 /**
     *上一段英文的意思是:当前线程必须拥有该Object的对象锁,
     *调用wait后,交出对象锁的控制权,然后等待这个Object对象调用notify来唤醒它
     */

     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }
This method should only be called by a thread that is the owner of this object‘s monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor.
 /**
     *  翻译:这段静态代码块只有获得了obj的对象锁之后的线程才能进行访问,
     *  去查看notify方法的解释来了解一个线程如何获得一个对象对象锁
     */
//再来看看notify的注解:
Wakes up a single thread that is waiting on this object‘s monitor. If any threads are waiting on this object, one of them is chosen to be awakened.  A thread waits on an object‘s monitor by calling one of the wait methods.
 /**
     *  唤醒一个正在等待该对象锁的线程。如果有正在等待的线程,
     *  就随机唤醒一个,**一个线程通过调用一个Object的wait方法来停止运行等待这个Object的监视器(对象锁)(这句话是重点!!)**
     *
     */
Only one thread at a time can own an object‘s monitor.
//一个对象的监视器在同一时间最多被一个线程获得

好了,我们通过对这些注解的阅读,一切一目了然了,我们来总结一下:

1.wait的作用:当前线程交出对象锁,并停止运行,等待再次获得这个对象锁(被哪个Object对象wait的,就必须得得到哪个Object对象的notify或者notifyAll方法才能运行)

2.notify的作用:随机唤醒一个线程,哪一个线程呢,就是被此Object对象wait的线程之一,即因调用了此Object对象而停止运行的线程。

1.notify和wait到底是属于线程,还是属于Object,它们的调用对线程又有着什么影响?

答:notify和wait属于Object,wait使线程停止运行并等待此对象的对象锁, notify,交出对象锁,并且唤醒一个正在等待的线程,一个正在等待该对象对象锁的线程。

问题二:

2.网上说notify和wait必须在synchronized代码块之内调用,为什么?

 synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }
This method should only be called by a thread that is the owner of this object‘s monitor. 

答:因为只有获得对象锁的线程才有资格wait和notify。而进入这段代码块的肯定具备了obj的对象锁,否则也进不去啊。至于为啥,看看上面那句英文吧,翻译过了。

最后画一张图:

啊,尽力了。。。现在还不明白的那就多看几遍吧。我的表述能力只能这样了,当然,你听懂了,我很高兴。恩,就是这样。

最后,给个比较经典的多线程同步例子,大家看一下,如果能看懂,那就说明你真学明白了:(运行结果 ABCABCABC……)

public class JavaThread implements Runnable {
    /**implements   extend
     * 实现线程的两种方式:
     * 1.实现runnable接口
     * 2.继承Thread类,实run方法:
     */

    private String name;
    private Object prev;
    private Object self;     

    private JavaThread(String name, Object prev, Object self) {
        this.name = name;
        this.prev = prev;
        this.self = self;
    }     

    @Override
    public void run() {
        int count = 10;
        while (count > 0) {
            synchronized (prev) {
                synchronized (self) {
                    System.out.println(name);
                    count--;
                    self.notify();
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        JavaThread pa = new JavaThread("A", c, a);
        JavaThread pb = new JavaThread("B", a, b);
        JavaThread pc = new JavaThread("C", b, c);
        new Thread(pa).start();
        Thread.sleep(100);
        new Thread(pb).start();
        Thread.sleep(100);
        new Thread(pc).start();
    }
} 

有问题请留言喽。

时间: 2024-11-08 23:33:10

synchronized静态代码块,以及其中的wait,notify和notifyAll的相关文章

从头认识多线程-2.17 同步方法与同步静态代码块持有的是不同的锁

这一章节我们来讨论一下同步方法与同步静态代码块持有的是不同的锁. 代码清单: package com.ray.deepintothread.ch02.topic_18; /** * * @author RayLee * */ public class SynchClass { public static void main(String[] args) throws InterruptedException { MyService myService = new MyService(); Thr

从头认识多线程-2.16 同步静态方法和静态代码块

这一章节我们来讨论一些同步静态方法和静态代码块. 代码清单 package com.ray.deepintothread.ch02.topic_17; /** * * @author RayLee * */ public class SynchClass { public static void main(String[] args) throws InterruptedException { ThreadOne threadOne = new ThreadOne(); Thread threa

java中的静态代码块、构造代码块、普通代码块和同步代码块总结

java中的4中代码块总结如下: * 加了static的是静态代码块,在类中写了一对{}是构造代码块,在方法中写了一对{}是普通代码块, * java中还有一种代码块是同步代码块,常用在多线程中, synchronized关键字, * 同步代码块格式是:synchronized(同步对象){} * 静态代码块 先于构造代码块 先于构造方法执行 * 静态代码块 先于普通代码块 先于构造方法执行 * 构造代码块和普通代码块按照程序逻辑顺序执行 package 面试题; class HelloA{ p

java中静态代码块的用法—— static用法详解

(一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用比如main方法就必须是静态的 这是程序入口两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的. 静态方法(1)在Java里,可以定义一个不需要创建对象的方法,这种方法就

JavaEE初始化时静态代码块加载问题

1.使用java.exe命令运行某个类的时java.exe Person2.创建一个类的对象时Person p=new Person();3.访问类中的静态成员变量(赋值/获取值)System.out.println(Person.className);4.访问了类中的静态成员方法Person.method();5.创建类的子类对象的时候Student s=new Student();6.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象System.out.printl

Java中普通代码块,构造代码块,静态代码块区别及代码示例

//普通代码块:在方法或语句中出现的{}就称为普通代码块.普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定--"先出现先执行"public class CodeBlock01{ public static void main(String[] args){ { int x=3; System.out.println("1,普通代码块内的变量x="+x); } int x=1; System.out.println("主方法内的变量x="

静态代码块、构造代码块、构造函数的执行

1 public class Str { 2 3 static { 4 System.out.println("static{1}"); 5 } 6 static { 7 System.out.println("static{2}"); 8 } 9 { 10 System.out.println("{}"); 11 } 12 13 Str() { 14 System.out.println("Str()"); 15 } 16

java普通代码块、静态代码块、默认构造方法的执行顺序

1 package test; 2 3 class Parent{ 4 { 5 System.out.println("父类普通代码块"); 6 } 7 static{ 8 System.out.println("父类静态代码块"); 9 } 10 public Parent(){ 11 System.out.println("父类默认构造代码方法"); 12 } 13 } 14 class Child extends Parent{ 15 {

Java误区: 静态代码块,当把类将被载入到自己主动运行?

JAVA静态代码块会在类被载入时自己主动运行? 非常多Java开发人员的思想,被这个思想深深的轮奸了n遍,传播这个错误思想的博客,在网上一堆,越来越多的人被轮奸. 如:http://blog.csdn.net/leeyu35/article/details/7755304 那么我们程序来证明这句话是错误的: class MyClass1 { static {//静态块 System.out.println("static block "); } } public class Main