深入理解Java锁(Synchronized)

想要解决多线程问题,首先我们需要掌握Java锁的原理。

我们常用锁Synchronized的方式

一:方法锁

1:方法锁锁的是该对象。例如:

public static void main(String[] arr) {
    final Test1 test1 = new Test1();
    for (int i = 0; i < 15; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                test1.send();
            }
        }).start();
    }
}
public class Test1 {
    int data;
    public synchronized void send() {
        System.out.println();
        System.out.println("方法入口******= "+
                Thread.currentThread().getName());
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        System.out.println("方法出口&&&&&&= "+
                Thread.currentThread().getName());
        data = 0;
    }
}

输出结果为:


说明线程处于排队状态,该方法只允许一个线程执行,执行完之后释放锁,线程重新获取锁执行。

2:去掉send方法上的锁 synchronized关键字

public class Test1 {
    int data;
    public  void send() {
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        data = 0;
    }
}

扫描二维码关注公众号,回复: 1676733 查看本文章
public class Test1 {
    int data;
    public  void send() {
        System.out.println();
        System.out.println("方法入口******= "+
                Thread.currentThread().getName());
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        System.out.println("方法出口&&&&&&= "+
                Thread.currentThread().getName());
        data = 0;
    }
}

输出结果为


由此可见,去掉锁,则其中一个线程在执行该方法时,其他线程依然可以执行。

3:当多个实例调用send方法时

public static void main(String[] arr) {
    for (int i = 0; i < 15; i++) {
        final Test1 test1 = new Test1();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test1.send();
            }
        }).start();
    }
}

public class Test1 {
    int data;
    public  void send() {
        System.out.println();
        System.out.println("方法入口******= "+
                Thread.currentThread().getName());
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        System.out.println("方法出口&&&&&&= "+
                Thread.currentThread().getName());
        data = 0;
    }
}

输出结果为:


所以由此说明,方法锁,锁的是当前实例。当有多个实例,锁不受影响。

二:代码块锁

该方式的锁,比较灵活。既可以锁当前实体,也可以锁定当前类。锁定当前实体,则跟方法锁一样,这里不再说明。重点说下锁定类。

1:锁定类。当锁定类的时候,不论是单一实体还是多实体的多线程请求该段上锁的代码块,都将抢锁,其他线程处于等待。

public static void main(String[] arr) {
    for (int i = 0; i < 10; i++) {
        final Test1 test1 = new Test1();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test1.send();
            }
        }).start();
    }
}
public class Test1 {
    int data;
    public  void send() {
       synchronized (Test1.class){
           System.out.println();
           System.out.println("方法入口******= "+
                   Thread.currentThread().getName());
           for (int i = 0; i < 20000; i++) {
               data++;
           }
           System.out.println(data);
           System.out.println("方法出口&&&&&&= "+
                   Thread.currentThread().getName());
           data = 0;
       }
    }
}

输出结果为:


由此说明,锁定类,即使是多实体的多线程,依然是有效果的。


总结:

1:当一个类中有多个方法锁时,具有方法锁的方法则彼此间不能互相调用,会出现死锁现象。

2:代码块锁中的锁,可以灵活变换,可以锁定其他实体,也可以锁定该片段为类的锁,使该类的多实体多线程抢用同一锁,其他线程处于等待状态。

3:代码块锁在一个线程抢占资源后,其他线程处于等待状态,而并不是该线程执行完之后其他线程才可执行,当一个线程for循环执行一个代码块锁。在执行一次后会释放锁,然后共同抢锁,有可能出现for循环只执行了一次,锁的资源被其他线程抢走了。所以在处理业务逻辑要特别注意。

猜你喜欢

转载自blog.csdn.net/weixin_41509424/article/details/79379429