synchronized与Lock

Java中解决公共变量访问的线程安全问题的主要的方法是加锁,包括synchronized、Lock

一、synchronized

1.1、synchronized 修饰目标

synchronized 可用于修饰方法(静态的、常规的)、代码块。其中,锁定代码块常用。

/**************************************************
 * synchronized 修饰静态方法(锁粒度太大,不常用)
 **************************************************/
public class Synchronized1 {

    //内部静态量
    private static int n = 0;

    //内部静态类
    private static class Counter{

        private static int MAX = 1000000;

        private synchronized static void increase() {
            for (int i = 0; i < MAX; i++) {
                n++;
            }
        }

        private synchronized static void decrease() {
            for (int i = 0; i < MAX; i++) {
                n--;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> { Counter.increase(); });
        thread1.start();

        Thread thread2 = new Thread(() -> { Counter.decrease(); });
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(n);
    }
}
/************************************************
 * synchronized 修饰普通方法(锁粒度大,不常用)
 ************************************************/
public class Synchronized2 {

    //内部静态量
    private static int n = 0;

    //内部静态类
    private static class Counter{

        private static int MAX = 1000000;

        private synchronized void increase() {
            for (int i = 0; i < MAX; i++) {
                n++;
            }
        }

        private synchronized void decrease() {
            for (int i = 0; i < MAX; i++) {
                n--;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Counter counter  = new Counter();
        Thread thread1 = new Thread(() -> { counter.increase(); });
        thread1.start();

        Thread thread2 = new Thread(() -> { counter.decrease(); });
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(n);
    }
}
/*******************************************
 *     synchronized 修饰代码块(常用)
 *******************************************/
public class Synchronized3 {

    // 内部静态量
    private static int n = 0;

    // 内部静态类
    private static class Counter {

        private static int MAX = 1000000;

        private void increase() {
            for (int i = 0; i < MAX; i++) {
                //synchronized (锁载体),方法里锁载体要一样
                synchronized (this) {
                    n++;
                }
            }
        }

        private void decrease() {
            for (int i = 0; i < MAX; i++) {
                //synchronized (锁载体),方法里锁载体要一样
                synchronized (this){
                    n--;
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();

        Thread thread1 = new Thread(() -> { counter.increase(); });
        thread1.start();

        Thread thread2 = new Thread(() -> { counter.decrease(); });
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(n);
    }
}

1.2、synchronized 加锁对象(锁载体)

synchronized 的加锁对象可以是【类】、【this对象】、【自定义对象】。其中,自定义对象常用。对于同一个业务的多个线程的加锁对象,需是同一对象(即加同一把锁),否则加锁无效。

/*******************************************
 *   synchronized 加锁对象为类、this对象
 *******************************************/
public class Synchronz {

    // 内部静态量
    private static int n = 0;

    // 内部静态类
    private static class Counter {

        private static int MAX = 1000000;

        private void increase() {
            for (int i = 0; i < MAX; i++) {
                //synchronized (锁载体),方法里锁载体要一样
                synchronized (this) {
                    n++;
                }
            }
        }

        private void decrease() {
            for (int i = 0; i < MAX; i++) {
                //synchronized (锁载体),方法里锁载体要一样
                synchronized (Synchronz.class){
                    n--;
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();

        Thread thread1 = new Thread(() -> { counter.increase(); });
        thread1.start();

        Thread thread2 = new Thread(() -> { counter.decrease(); });
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(n);
    }
}
/*******************************************
 *     synchronized 加锁对象为自定义对象
 *******************************************/
public class Synchronz2 {

    // 内部静态量
    private static int n = 0;
    private static Object obj = new Object();

    // 内部静态类
    private static class Counter {

        private static int MAX = 1000000;

        private void increase() {
            for (int i = 0; i < MAX; i++) {
                //synchronized (锁载体),方法里锁载体要一样
                synchronized (obj) {
                    n++;
                }
            }
        }

        private void decrease() {
            for (int i = 0; i < MAX; i++) {
                //synchronized (锁载体),方法里锁载体要一样
                synchronized (obj){
                    n--;
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();

        Thread thread1 = new Thread(() -> { counter.increase(); });
        thread1.start();

        Thread thread2 = new Thread(() -> { counter.decrease(); });
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(n);
    }
}

1、synchronized是非公平锁(来的早不如来的巧),具有互斥性、排他性:线程执行到某个对象的 synchronized 时占用锁,其他线程如果也执行到同一对象 synchronized 发现锁占用,会自旋尝试获取锁一段时间,失败后进入休眠状态(排队)等待被操作系统唤醒。
2、synchronized 的锁信息存储在Java对象头里(隐藏属性):Java对象有个隐藏的对象头,其中存储了当前锁的状态标识(占用or空闲),同时还存储了占用当前锁的线程ID;

1.3、synchronized 实现原理

①在代码层面:
synchronized加锁的对象里有一个的隐藏的对象头,这个对象头(可看作一个类)里有很多属性,其中比较关注的两个属性是:是否加锁的标识和占有当前锁的线程id。每次进⼊ synchronized 修饰的代码块时,会去对象头中先判断加锁的标识,再判断拥有当前锁的线程id,从而决定当前线程能否继续执行。
◆判断加锁标识为false,即对象头未加锁,当前线程可以进入synchronized 修饰的代码块,并设置加锁标识为true,设置拥有当前锁的线程id为此线程id。
◆判断加锁标识为true,即对象头已加锁,需进一步判断拥有当前锁的线程id是否为此线程id,若是,则继续往下执行(即可重入);否则,不能往下执行,需要等待锁资源释放后重新竞争再获取锁。

②在JVM和操作系统层面:
synchronized同步锁是通过JVM内置的Monitor监视器实现的,而监视器又是依赖操作系统的互斥锁Mutex实现的。

二、Lock

/**
 * *.lock()要放到try外(官建)或try首行
 * *.unlock()要放在finally里,防止锁资源永久占用
 */
public class LockTest {

    private static int n = 0;

    private static class Counter{

        private static Lock lock = new ReentrantLock();
        private static int MAX = 1000000;

        private static void increase() {
            for (int i = 0; i < MAX; i++) {
                // 加锁
                lock.lock();
                try{
                    n++;
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        }

        private static void decr() {
            for (int i = 0; i < MAX; i++) {
                // 加锁
                lock.lock();
                try{
                    n--;
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> { Counter.increase(); });
        thread1.start();

        Thread thread2 = new Thread(() -> { Counter.decr(); });
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println(n);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值