小言_互联网的博客

用内存模型解析一下线程同步 (.wait()函数和.nottify()函数和synchronized关键字)

377人阅读  评论(0)

什么是内存模型??

public class Thread_Wait_Notify_02 implements Runnable{

    private String name;
    private Object pre;
    private Object self;
    private Object ne;

    public Thread_Wait_Notify_02(String name, Object pre, Object self,Object ne) {
        this.name = name;
        this.pre = pre;
        this.self = self;
        this.ne =ne;
    }
    @Override
    public void run() {

        int count=10;
        while (count>0){
            synchronized (pre){
                synchronized (self){
                    System.out.print(name);
                    count--;
                    //System.out.print(self);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    self.notify();
                }
                try {
                    pre.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
        synchronized (self){
            self.notify();
        }

    }
}

public class main {

    public static void main(String[] strings) throws InterruptedException {
        Object a=new Object();
        Object b=new Object();
        Object c=new Object();
        new Thread(){
           @Override
           public void run(){
           }
        };//不启动

        Thread_Wait_Notify_ pa=new Thread_Wait_Notify_("A",c,a,b);
        Thread_Wait_Notify_ pb=new Thread_Wait_Notify_("B",a,b,c);
        Thread_Wait_Notify_ pc=new Thread_Wait_Notify_("C",b,c,a);
        new Thread(pa,"A").start();
        Thread.sleep(1000*2);//确保按ABC这个顺序执行
        new Thread(pb,"B").start();
        //Thread.sleep(100);
        new Thread(pc,"C").start();
       // Thread.sleep(100);
        //System.out.println(c);

        //Thread_Wait_Notify_02 thread_wait_notify_02=new Thread_Wait_Notify_02("B",a,b,c);
        //new Thread(thread_wait_notify_02,"第二种实体对象线程").start();

    }

}

看输出::
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51
Ajava.lang.Object@75c85cajava.lang.Object@99b6b51java.lang.Object@da4eeba
Bjava.lang.Object@99b6b51java.lang.Object@da4eebajava.lang.Object@75c85ca
Cjava.lang.Object@da4eebajava.lang.Object@75c85cajava.lang.Object@99b6b51

我的理解:结合深入理解jvm书中P363~P365中的内容
每一个线程的栈帧在编译的时候就已经确定,也就是说这个线程内有多少个变量都是确定了的,上面的代码就是Thread_Wait_Notify_02和Thread_Wait_Notify_这两个类的成员属性都是一个线程的栈变量,main线程里面的的new Thread_Wait_Notify_(“A”,c,a,b),就会在内存里面新建一个线程,“A”,c,a,b,是在主线程传给这个线程的数据,“A”在方法区里面,c,a,b传入的是Java堆上的对象本身,也就是Java内存模型上的主内存。
main线程里面.start()后线程就开始运行了每一个用户线程必须有一条线程启动他),main线程的调用线程工作完成,例如上面的代码的A线程进入自己的run()方法执行,synchronized (self)这句话意思很重要:self本身也就是在java堆上的也就是内存模型所说的主内存,A线程会给这个“本身”lock(见JVM书中P364)加锁,read->load->复制副本到自己的线程的工作内存里面,这个时候其他的线程不能访问这个主内存的“本身”。
锁着了主内存的一个“本身之后”,接着按照上面的代码,System.out.print(name);这句话输出了方法区里面的值“A”(name是这个线程栈帧上的变量,指向方法区的“A”)。self.notify(),这句话的涵义:如果主内存里面原来有因为self指向的“本身”而进入等待状态的线程唤醒了(注意注意!!!!这个时候被唤醒的的线程不会立刻往下执行,而是处于一个锁池状态,因为唤醒他的线程的synchronized{}代码块还没结束,只有synchronized结束了唤醒线程才能往下执行)。见下图:

接着往下解析A线程, pre.wait();这句话也超级重要!!!!!!前面已经有了 synchronized (pre)把主内存上的pre的本身已经锁住了,执行了pre.wait();后会释放掉A线程上自己从主内存上锁住的所有“本身”,同时同时,自身(A线程)进入等待状态,也就是相当于这个线程不往下执行了,停在了那里,等待拯救它的线程执行这个pre指向的在主内存的“本身”执行.notify()语句,这样子才会A线程唤醒继续往下面执行。到此为止,A线程成功执行了while循环里面的第一个循环,输出了第一次,自己在等待它的救世主!!!!
这时回到main主线程,这个线程继续new出一个线程B线程,并且.start(),同理,C线程也是如此。每一条线程都是按上述的重复着,一直在解析Thread_Wait_Notify_这个类,Main主线程都是用这个类来新建线程调用线程。3个子线程在在总体上是交替执行的,每一个时刻只有一条线程在执行,另外两条是在等待状态,所以实现了ABCABC…循环输出,在总体上就实现了同步完成了这个功能,三个子线程各司其职,A线程只负责打印A,B线程只负责打印B,C线程只打印C。。。

体会一下没有加锁输出

我们提了这么多锁都是在线程之间传递的数据上加锁,在内存模型上就是主内存上的线程共享的”本身“,比如上面main线程声明的变量a,b,c指向的java堆”本身“。比如在MyThread02的成员变量属性或者在run()方法内声明的变量指向的java堆”本身“,没有涉及到传到其他线程上就没必要加锁。

没有加锁:在语言层面就是没有加synchronized关键字,在内存模型中就是在线程复制主内存时没有进行lock()操作,这个时候对线程来说就是竞争了,竞争的结果是未知的,随机的,完全由cpu来决定。看下面的代码:
解析:Runnable接口内的run()方法如果没有加上synchronized那就是没有给方法的参数指向的”本身“加锁,main主线程启动了两个子线程, String obj=new String(“主线程变量”);//"主线程的变量"分别传给了两个子线程,如果两个线程都没有加锁的话那就是看竞争状态了,随机的,看输出就知道,每一次输出都不一样,完全看cpu的状态。。。加了synchronized后,是线程1先加的锁那就要等线程一先执行完了再到线程2加锁。。所以输出是有序的。

public class MyThread02 implements Runnable {
    public MyThread02(String name) {
        this.name = name;
    }

    private String name;

    @Override
    public  void run() {
        for (int i=0;i<3;i++){
            System.out.println(name+i);

        }
    }
}


public class main02 {

    public static void main(String[] arg){

            /*Thread thread = new Thread(new MyThread02("C"));
        Thread thread01= new Thread(new MyThread02("D"));
        Thread.start()报错
        */
        String obj=new String("主线程变量");//"主线程的变量";
        String name="hah";
        /*synchronized (name){

        }*/
        new Thread(new MyThread02(obj+"+1")).start();
        new Thread(new MyThread02(obj+"+2")).start();
        //System.out.println(   new Thread());

    }
}

run()方法没有加synchronized的输出:
主线程变量+10
主线程变量+20
主线程变量+11
主线程变量+21
主线程变量+22
主线程变量+12

run()方法加了synchronized的输出:
主线程变量+10
主线程变量+11
主线程变量+12
主线程变量+20
主线程变量+21
主线程变量+22


转载:https://blog.csdn.net/weixin_42765975/article/details/102484091
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场