Java面试知识点(三十九)深入理解synchronized

1.synchronize关键字用于加锁,用来解决多线程中,多个线程对同一资源访问所造成的数据不安全的问题
2.使用synchronized修饰,相当于加了一把锁,对于synchronized锁定的资源,同一时间,只能有一个线程访问,保证了原子性。

线程不安全的代码示例:

class Ticket implements Runnable {
    private int num;    //票的数量

    Ticket(int num){
        this.num = num;
    }

    //售票
    public void sale() {
        if(num>0) {
            num--;
            System.out.println(Thread.currentThread().getName()+"-------"+remain());
        }
    }

    //获取剩余票数
    public int remain() {
        return num;
    }

    public void run(){
        while(true) {
            sale();
        }
    }
}

public class ConcurrentDemo {
    public static void main(String[] args) {
        Ticket t = new Ticket(100);
        //创建多个线程对象
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);

        //开启多个线程使其执行任务
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

结果:
Java面试知识点(三十九)深入理解synchronized_第1张图片

但是当synchronized关键字,也就是加锁,则可以避免这种问题


synchronized的使用

1. 修饰方法

public synchronized void sale() {  
        if(num>0) {
            num--;
         
            System.out.println(Thread.currentThread().getName()+"===sale2==========="+remain());
        }
    }
  • 在方法前面加上synchronized关键字表示作用于该方法
  • 需要注意方法有两种,一种静态方法,一种非静态方法
  • 两者区别在于,当修饰静态时候,大家都调用的是同一个。当修饰非静态方法时候,调用的是每个对象自己的那个方法,因为非静态域或方法是每个对象各自都有一份的,静态方法是所有对象公有的。

2. 修饰对象

修饰当前对象

public void sale() {
        synchronized(this) {  
            if(num>0) {
                num--;
                
                System.out.println(Thread.currentThread().getName()+"===sale1==="+remain());
            }
        }
    }

修饰类对象

public void sale() {
        synchronized(Ticket.class) {  
            if(num>0) {
                num--;
                
                System.out.println(Thread.currentThread().getName()+"===sale1==="+remain());
            }
        }
    }
  • synchronized 函数的本质其实是使用了 () 里面的对象 作为这个同步函数的锁标识,this 代表的是当前对象的引用,Ticket.class代表是类对象。
  • 如果同步函数是静态的,即使用了 static 修饰,则此时 this 还没出现,它使用的锁是 “类名.class” 这个字节码文件对象,对于 java 来说,这也是一个对象,而且一个类中一定有这个对象。
  • 使用相同的锁之间会互斥,但不同锁之间则没有任何影响。因此,要保证任务同步 (原子性),这些任务所关联的锁必须相同。也因此,如果有多个同步任务 (各自保证自己的同步性),就一定不能都使用同步函数。

锁的种类

1. 类锁

类锁,是用来锁类的,我们知道一个类的所有对象共享一个 class 对象,共享一组静态方法,类锁的作用就是使持有者可以同步地调用静态方法。当 synchronized 指定修饰静态方法或者 class 对象的时候,拿到的就是类锁,类锁是所有对象共同争抢一把。

2. 对象锁

对象锁,是用来对象的,虚拟机为每个的非静态方法和非静态域都分配了自己的空间,不像静态方法和静态域,是所有对象共用一组。
所以 synchronized 修饰非静态方法或者 this 的时候拿到的就是对象锁,对象锁是每个对象各有一把的,即同一个类如果有两个对象。那么这两个对象是并行执行的(线程不安全),即使方法加了对象锁。


你可能感兴趣的:(java,面试,Java面试知识汇总)