volatile
概念性
volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块。该关键字可以确保对一个变量的更新对其他的线程马上可见,当一个变量被声明为volatile的时候,线程在写入变量时不会把值的缓存写在寄存器或者其他的地方,而是会把值刷回到主内存中,当其他线程读取这个变量的时候,会从主内存中重新获取最新值,而不是当期线程的工作内存中的值。
在什么时候使用volatitle
1:写入的变量值不依赖变了的当前值,因为如果依赖当前的值,将是获取-计算-写入三个步骤。这三个步骤不是原子操作,而volatitle不支持原子性。
2:读写变量的时候没有加锁。因为加锁本身已经保证了内存的可见性,这时候不需要进行再次声明
指令重排
在我们系统字节码进行编译的时候会对不存在数据依赖的指令进行重新排列,以达到提高运行性能。 volatile可以禁止指令重排,在用volatile修饰的变量声明的时候,会加一个内存屏障,使后面的命令无法在其之前操作。这就严格保证了语法的执行顺序。保证了有序性
sychronized
概念性
sychronized可以理解为原子性内置锁,当线程访问的时候,首先会去获得这个内置锁。而其他的线程访问的时候,则需要阻塞等待,因此sychonized是排他锁,可以使用他来修饰代码块和方法。
内存语义
进入到sychronized的内存语义是把在sychronized块使用到的变量从线程的工作内存中清除,这样在sychronized块使用该变量的时候就不会从线程的工作内存中获取,而是直接从主内存中获取;退出的时候sychronied的语义是把在sychronied块内存对共享变量的值修改刷新到主内存中。可以理解为加锁的目的是清除本地缓存,从主存中获取值,释放锁的目的是为了将值刷新到主内存中。sychronized经常被用来实现原子性的操作,但是使用的时候需要注意频繁的使用会导致线程上下文切换的开销。
ReentrantLock
概念
ReentrantLock是可重入的独占锁,同时只有一个线程获取锁,其他的线程则会被阻塞到AQS队列中。相比于sychronized的好处是可以手动抛出我们需要的异常信息。但是不方便的是我们必须每次去释放锁,如果不释放锁的话,可能导致我们的资源浪费,以及其他线程无法修改锁定的处理变量。ReentrantLock内部有公平和非公平锁的实现,默认情况下我们认为的非公平锁。
几个方法
1:void lock() 当一个线程使用该方法的时候,说明该线程希望获得锁。如果未被占用则获得锁,并且AQS的状态置为1,直接返回,如果是已经获得锁,则进行AQS加一后返回,否则则进入AQS阻塞队列进行阻塞
2:void lockInterruptibly() 和lock方法一样。不过这个方法可以响应异常,如果当前线程在调用该方法的时候,其他线程调用了当前线程的interrupt()方法,则当前线程会抛出异常。返回
3:boolean tryLock() 尝试获得锁,如果该锁没有被求他线程持有,则当前线程获得该锁,并且返回true。否则返回false。这个方法不会引起阻塞。
4:boolean tryLock(long timeOut,TimeUnit unit) 尝试获得锁,和tryLock一样,不同的是设置了超时时间,如果时间到了,则抛出false。
5:void unlock() 尝试释放锁,如果当期线程持有的锁,然后对AQS减一。如果AQS=0。则释放锁,如果不为0,则仅仅减一,如果当期线程没有持有锁,则抛出异常