携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
同步机制的几种方式
出现线程安全问题:
如果存在多个线程对共享资源竞争,就可能发生线程安全问题。
一般解决线程安全问题,需要➕锁
1. synchronized同步方法
对于非static方法加上synchronized,是对当前对象加锁。而如果对static方法加上synchronized关键字,是对当前类对象-class对象加锁。
-
如果一个线程访问一个对象的synchronized方法,那么其它线程不能访问该对象的synchronized方法。
-
但是其它线程可以访问这个对象的非synchronized方法。
-
当然,对于一个类的不同实例之间互不影响。
-
如果一个线程执行一个对象的非static synchronized方法,另外一个线程执行这个对象所属类的static synchronized方法,不会互斥。因为一个是对象锁,一个是类锁。
-
对于synchronized同步代码块或者同步方法,当出现异常的时候,虚拟机会自动释放当前线程占用的锁,因此不会出现由于异常导致出现死锁现象。
synchronized原理
对于同步代码块用javap -c com.demo.synchronzeddemo反编译成字节码的时候,会在同步块的入口和出口分看到monitorenter,monitorexit ,这两个指令。
其实对于每个对象都有一个监视器锁,monitor,当monitor被占用的时候就会处于锁定状态,线程执行monitorenter的时候会尝试获取monitor的所有权。
-
如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程为monitor的所有者;
-
如果线程为该monitor的所有者,重新进入,则进入monitor的进入数+1;
-
如果其它线程占用了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^)~~~