回顾前面:html
只有光头才能变强!java
本文章主要讲的是Java多线程加锁机制,有两种:编程
不得不唠叨几句:c#
其实都比较坑,若是能先系统讲了Synchronized锁机制,接着讲显式Lock锁机制,那就很容易理解了。也不须要跨那么多章节。安全
那么接下来咱们就开始吧~微信
synchronized是Java的一个关键字,它可以将代码块(方法)锁起来多线程
public synchronized void test() {
// 关注公众号Java3y
// doSomething
}
复制代码
synchronized是一种互斥锁并发
synchronized是一种内置锁/监视器锁post
Java中的synchronized,经过使用内置锁,来实现对变量的同步操做,进而实现了对变量操做的原子性和其余线程对变量的可见性,从而确保了并发状况下的线程安全。性能
咱们首先来看一段synchronized修饰方法和代码块的代码:
public class Main {
//修饰方法
public synchronized void test1(){
}
public void test2(){
// 修饰代码块
synchronized (this){
}
}
}
复制代码
来反编译看一下:
同步代码块:
同步方法(在这看不出来须要看JVM底层实现)
synchronized底层是是经过monitor对象,对象有本身的对象头,存储了不少信息,其中一个信息标示是被哪一个线程持有。
具体可参考:
synchronized通常咱们用来修饰三种东西:
用的锁是Java3y对象(内置锁)
public class Java3y {
// 修饰普通方法,此时用的锁是Java3y对象(内置锁)
public synchronized void test() {
// 关注公众号Java3y
// doSomething
}
}
复制代码
用的锁是Java3y对象(内置锁)--->this
public class Java3y {
public void test() {
// 修饰代码块,此时用的锁是Java3y对象(内置锁)--->this
synchronized (this){
// 关注公众号Java3y
// doSomething
}
}
}
复制代码
固然了,咱们使用synchronized修饰代码块时未必使用this,还能够使用其余的对象(随便一个对象都有一个内置锁)
因此,咱们能够这样干:
public class Java3y {
// 使用object做为锁(任何对象都有对应的锁标记,object也不例外)
private Object object = new Object();
public void test() {
// 修饰代码块,此时用的锁是本身建立的锁Object
synchronized (object){
// 关注公众号Java3y
// doSomething
}
}
}
复制代码
上面那种方式(随便使用一个对象做为锁)在书上称之为-->客户端锁,这是不建议使用的。
书上想要实现的功能是:给ArrayList添加一个putIfAbsent()
,这须要是线程安全的。
假定直接添加synchronized是不可行的
使用客户端锁,会将当前的实现与本来的list耦合了:
书上给出的办法是使用组合的方式(也就是装饰器模式)
获取到的是类锁(类的字节码文件对象):Java3y.class
public class Java3y {
// 修饰静态方法代码块,静态方法属于类方法,它属于这个类,获取到的锁是属于类的锁(类的字节码文件对象)-->Java3y.class
public synchronized void test() {
// 关注公众号Java3y
// doSomething
}
}
复制代码
synchronized修饰静态方法获取的是类锁(类的字节码文件对象),synchronized修饰普通方法或代码块获取的是对象锁。
public class SynchoronizedDemo {
//synchronized修饰非静态方法
public synchronized void function() throws InterruptedException {
for (int i = 0; i <3; i++) {
Thread.sleep(1000);
System.out.println("function running...");
}
}
//synchronized修饰静态方法
public static synchronized void staticFunction() throws InterruptedException {
for (int i = 0; i < 3; i++) {
Thread.sleep(1000);
System.out.println("Static function running...");
}
}
public static void main(String[] args) {
final SynchoronizedDemo demo = new SynchoronizedDemo();
// 建立线程执行静态方法
Thread t1 = new Thread(() -> {
try {
staticFunction();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 建立线程执行实例方法
Thread t2 = new Thread(() -> {
try {
demo.function();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 启动
t1.start();
t2.start();
}
}
复制代码
结果证实:类锁和对象锁是不会冲突的!
咱们来看下面的代码:
public class Widget {
// 锁住了
public synchronized void doSomething() {
...
}
}
public class LoggingWidget extends Widget {
// 锁住了
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
复制代码
doSomething()
方法时,此时拿到了LoggingWidget实例对象的锁。doSomething()
方法,它又是被synchronized修饰。doSomething()
方法还须要一把锁吗?不须要的!
由于锁的持有者是“线程”,而不是“调用”。线程A已是有了LoggingWidget实例对象的锁了,当再须要的时候能够继续**“开锁”**进去的!
这就是内置锁的可重入性。
Lock显式锁是JDK1.5以后才有的,以前咱们都是使用Synchronized锁来使线程安全的~
Lock显式锁是一个接口,咱们来看看:
随便翻译一下他的顶部注释,看看是干吗用的:
能够简单归纳一下:
前面说了,Lock显式锁给咱们的程序带来了不少的灵活性,不少特性都是Synchronized锁没有的。那Synchronized锁有没有存在的必要??
必须是有的!!Lock锁在刚出来的时候不少性能方面都比Synchronized锁要好,可是从JDK1.6开始Synchronized锁就作了各类的优化(毕竟亲儿子,牛逼)
因此,到如今Lock锁和Synchronized锁的性能其实差异不是很大!而Synchronized锁用起来又特别简单。Lock锁还得顾忌到它的特性,要手动释放锁才行(若是忘了释放,这就是一个隐患)
因此说,咱们绝大部分时候仍是会使用Synchronized锁,用到了Lock锁说起的特性,带来的灵活性才会考虑使用Lock显式锁~
公平锁理解起来很是简单:
非公平锁就是:
Lock和synchronize都是默认使用非公平锁的。若是不是必要的状况下,不要使用公平锁
本文讲了synchronized内置锁和简单描述了一下Lock显式锁,总得来讲:
Lock锁这里只是介绍了一些些,明天会详解它的相关子类和须要注意的地方,敬请期待~
以前在学习操做系统的时候根据《计算机操做系统-汤小丹》这本书也作了一点点笔记,都是比较浅显的知识点。或许对你们有帮助
参考资料:
若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同窗,能够关注微信公众号:Java3y。
文章的目录导航: