Java并发 - synchronized详解一


前言

打完篮球好困,大家搞程序员的也不能总坐着啊,也要运动运动的 - -


提示:以下是本篇文章正文内容,下面案例可供参考

一、 synchronized的使用

对象锁

包括方法锁(默认锁对象为this,当前实例对象)和同步代码块锁(自己指定锁对象)

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instance = new SynchronizedObjectLock();

    @Override
    public void run() {
        // 同步代码块形式——锁为this,两个线程使用的锁是一样的,线程1必须要等到线程0释放了该锁后,才能执行
        synchronized (this) {
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "结束");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
    }
}

输出结果为:

我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束

两个线程使用的为一个实例,用的是同一把锁,所以线程t2必须等到线程t1释放之后才能获取锁

代码块形式:手动指定锁对象,可以使this也可以是自定义的锁

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instance = new SynchronizedObjectLock();

    @Override
    public void run() {
        // 同步代码块形式——锁为this,两个线程使用的锁是一样的,线程1必须要等到线程0释放了该锁后,才能执行
        synchronized (this) {
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "结束");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
    }
}

输出结果:

我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束

方法锁形式:修饰普通方法,锁对象默认为this

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instance = new SynchronizedObjectLock();

    @Override
    public void run() {
        method();
    }

    public synchronized void method() {
        System.out.println("我是线程" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "结束");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
    }
}
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束

修饰普通方法= 锁this,用的是同一个实例,所以要等待线程1 执行完毕才会执行线程2

类锁

直接锁class对象

所有线程需要的锁 都为一把。

修饰静态方法

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instance1 = new SynchronizedObjectLock();
    static SynchronizedObjectLock instance2 = new SynchronizedObjectLock();

    @Override
    public void run() {
        method();
    }

    // synchronized用在静态方法上,默认的锁就是当前所在的Class类,所以无论是哪个线程访问它,需要的锁都只有一把
    public static synchronized void method() {
        System.out.println("我是线程" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "结束");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
    }
}

修饰静态方法时,锁的是class类,无论哪个实例线程,需要的锁都是同一把,需等待前一把释放才可获取

输出结果为:

我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束

二、Synchronized原理分析

加锁和释放锁的原理

MonitorenterMonitorexit指令,会让对象在执行,使其锁计数器加1或者减1。每一个对象在同一时间只与一个monitor(锁)相关联,而一个monitor在同一时间只能被一个线程获得,一个对象在尝试获得与这个对象相关联的Monitor锁的所有权的时候,monitorenter指令会发生如下3中情况之一:

  • monitor计数器为0,意味着目前还没有被获得,那这个线程会立即获得,并将计数器加一。一旦加一,别的线程就别想获取,需要等待。
  • 如果这个monitor已经拿到了锁的所有权,又重入了这把锁,那么就会继续+1,每重入一下,都会+1,会一直累加。
  • 这把锁已经被别的线程获取了,等待释放。

monitorexit指令:释放对于monitor的所有权,释放过程很简单,就是讲monitor的计数器减1,如果减完以后,计数器不是0,则代表刚才是重入进来的,当前线程还继续持有这把锁的所有权,如果计数器变成0,则代表当前线程不再拥有该monitor的所有权,即释放锁。

可重入锁

指在外层方法中已经获取了对象锁,执行内部方法时会自动获取锁 ,不会因为之前已经获取过还没释放而阻塞(前提是锁对象是同一个对象或者class)
所以不难理解

  • 执行monitorEnter获取锁:
    重入则monitor计数器+1
  • 执行monitorexit命令
    monitor计数器-1,计数器=0 锁被释放。

总结

对于锁方法,静态方法,class对象 应分辨清楚

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静为躁君S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值