Java中同步是如何来实现的——小布

70 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

Spring.jpg

同步机制的几种方式

出现线程安全问题:

如果存在多个线程对共享资源竞争,就可能发生线程安全问题。

一般解决线程安全问题,需要➕锁

1. synchronized同步方法

对于非static方法加上synchronized,是对当前对象加锁。而如果对static方法加上synchronized关键字,是对当前类对象-class对象加锁。

  1. 如果一个线程访问一个对象的synchronized方法,那么其它线程不能访问该对象的synchronized方法。

  2. 但是其它线程可以访问这个对象的非synchronized方法。

  3. 当然,对于一个类的不同实例之间互不影响。

  4. 如果一个线程执行一个对象的非static synchronized方法,另外一个线程执行这个对象所属类的static synchronized方法,不会互斥。因为一个是对象锁,一个是类锁。

  5. 对于synchronized同步代码块或者同步方法,当出现异常的时候,虚拟机会自动释放当前线程占用的锁,因此不会出现由于异常导致出现死锁现象。

synchronized原理

对于同步代码块用javap -c com.demo.synchronzeddemo反编译成字节码的时候,会在同步块的入口和出口分看到monitorenter,monitorexit ,这两个指令。

其实对于每个对象都有一个监视器锁,monitor,当monitor被占用的时候就会处于锁定状态,线程执行monitorenter的时候会尝试获取monitor的所有权。

  1. 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程为monitor的所有者;

  2. 如果线程为该monitor的所有者,重新进入,则进入monitor的进入数+1;

  3. 如果其它线程占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,则重新尝试获取monitor的所有权。(非公平锁)

java内存模型规定所有的变量都存在于主存,每个线程都有自己的工作内存。线程对于变量的所有操作都必须在自己的工作内存内,不能直接对主存操作。每个线程都不能访问别的线程的工作内存。

并发编程的三个概念:原子性,可见性,有序性

原子性:java内存模型保证了基本读取和赋值是原子操作。

可见性:volatile关键字保证了可见性

有序性:指令重排序不会影响单线程程序的运行,但是会影响多线程安全。

我的理解是,必须是满足原子性

1. 状态标记量;

2. 双重检验。代码示例:

class Singleton{

private volatile static Singleton instance = null;

private Singleton{

}

public static Singleton getInstance(){

if(instance == null){

synchronized(Singleton.class){

if(instance == null){

instance = new Singleton();}}}

return instance;}

}

lock锁

synchronized的缺陷

对于synchronized锁来说,只有执行完毕或者异常才会释放锁,否则一直堵塞,且没办法中断等待;

同时对于synchronized锁来说,无论是读还写,同一时段都只有一个线程可以做到,对于很多读线程,效率低下。

java.util.concurrent.locks包下的lock

Lock是一个接口,里面定义了5个方法,主要是获得锁和释放锁。

其中lock()的用法如右图所示。

tryLock()会返回,无论是否拿到锁都会立刻返回。

tryLock(long time, timeout unit)会等待一段时间。

lockInterruptibly(),当通过这个方法去获得锁的时候,如果线程正在等待锁,可以响应中断。

以上就是本人对于Java中同步实现方式的个人见解,今天的分享就到这,我们下期再见😀~~~///(^v^)~~~