Java多线程6 中同步函数的锁和同步代码块的锁的区别

同步代码块的出现是解决了多线程的安全问题,但是它增加了代码的缩进层级同时降低了效率(每次无论是不是对的锁,每个路径都要去判断)

针对同步出现的这两个问题,首先讨论第一个。因此引出一个新的知识点————————

同步函数

关于同步函数的使用(一买车票的代码为例子)代码:

package Thread;
class Tickets implements Runnable{
    private int ticket=100;
    Object obj = new Object();
    public void run(){
        while(ticket!=0) {//多线程里要有循环
                sale();
        }
    }
    public synchronized void sale(){
        if (ticket > 0) {
            //使CPU在这里停一下,使用sleep()函数,Thread 中的静态函数。使其出现错误的数据
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }//没有解决方案
            System.out.println(Thread.currentThread().getName() + " " + --ticket);
        }
    }
}

public class ThreadSellTickets {
    public static void main(String[] args){
        Tickets tc = new Tickets();
        Thread T1  =new Thread(tc);
        Thread T2  =new Thread(tc);
       // Thread T3  =new Thread(tc);
        //Thread T4  =new Thread(tc);
        T1.start();
        T2.start();
        //T3.start();
        //T4.start();
    }
}

同步函数:用synchronized  修饰的函数,因为函数是被对象调用的,所以在使用同步函数时:this.函数名。

在函数调用时其实最完整的其实就是this.函数名

验证写法就是方法:(验证同步函数的所是否为this)

开启两条线程,一条执行同步代码块,一条执行同步函数,如果执行结果没有错误,证明所用的对象/锁  是一致的,如果执行出现了错误证明所用的对象不一致

代码:(完整正确的代码)

package Thread;
class Ticket implements Runnable{
    private int ticket=100;
    boolean flag=true;
    Object obj = new Object();
    public void run(){
       if(flag){
           for(int x=1;x<=5;x++){
               synchronized (this){
                   if(ticket>0){
                       try{Thread.sleep(10);}catch(InterruptedException e){}
                       System.out.println(Thread.currentThread().getName()+" ticket = "+--ticket);
                   }

               }
           }
       }
       else{
           for(int y=1;y<=5;y++) {
               sale();
           }
       }
    }
    public synchronized  void sale(){
        if(ticket>0){
            try{Thread.sleep(10);}catch(InterruptedException e){}
            System.out.println(Thread.currentThread().getName()+" ticket = "+--ticket);
        }
    }
}
public class Differents {
    public static void main(String[] args){
        Ticket t = new Ticket();
        Thread T1 =new Thread(t);
        Thread T2 =new Thread(t);
        T1.start();
        try{Thread.sleep(10);}catch(InterruptedException e){}//使主线程停一会此时只有线程0 在运行
        t.flag=false;
        T2.start();
    }
}

关于这段代码的解释:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

首先明确CPU切换的意思:CPU是切换到线程,CPU切换到那个线程,哪一个线程就往下执行(指哪里,哪里跑)

解释线程开启间加入休眠函数,避免线程0开启后flag 立刻被改变,并且主线程开启线程1

这段代码run 函数中换成无限循环更好理解这段代码:

flag = true ; 线程0 会一直执行同步代码块,线程0 执行一段时间后,flag 被改变,线程1被开启,线程1执行的是同步函数。

如果两个锁不一样,当线程0执行同步代码块中代码到一半时CPU切换到线程1,线程1 可以在同步函数中对共享数据进行修改——数据会出现错误。

如果两个锁(为this )是一样的,线程0,执行同步代码块执行到一半时CPU切换到线程1,线程一无法进入同步函数,因为锁对象还在线程0那

代码同步代码块和同步函数的异同:

同:都是为同步服务,为了解决多线程的安全问题

异:锁不一样

同步代码块的对象/锁 是自定义的任意对象
,通过锁来区别同步。是更加常见的

       同步函数的对象/锁   是固定的 this 。因此只需要一个同步时可以使用同步函数

静态同步函数使用的锁不是this 而是类所对应的.class 文件(字节码文件对象):类名.class (按照万物皆对象的原则这个文件其实也是一个对象)

解释:静态是随着类的加载而加载的,静态是被类名调用的。类加载后的.class 文件其实就是他的一个对象

猜你喜欢

转载自blog.csdn.net/Stitch__/article/details/81166759