概念
synchronized 是 Java 中的关键字,是利用 Mutx 互斥锁的机制来实现同步的,底层采用 Futex 锁进行控制,实现原理为 fast mutex 互斥锁,因为他可以动态模糊用户空间以及内核空间的转换,所以非常方便,命名为 fast 快速。
锁机制有如下两种特性:
对象锁和类锁
对象锁
在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。
类锁
在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。
synchronized 的用法分类
根据修饰对象分类
synchronized 可以修饰方法和代码块
根据获取的锁分类
synchronized 的用法详解
这里根据获取的锁分类来分析 synchronized 的用法
对象锁
这个对象是新建的,跟其他对象没有任何关系:
/**
* synchronized 修饰非静态方法
*/
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (new SyncThread()) {
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
文章后面所有例子都是采用这四个线程,只是对方法进行修改:
SyncThread syncThread = new SyncThread();
Thread F_thread1 = new Thread(new SyncThread(), "F_thread1");
Thread F_thread2 = new Thread(new SyncThread(), "F_thread2");
Thread F_thread3 = new Thread(syncThread, "F_thread3");
Thread F_thread4 = new Thread(syncThread, "F_thread4");
F_thread1.start();
F_thread2.start();
F_thread3.start();
F_thread4.start();
复制代码
运行结果如下:
四个线程同时开始,同时结束,因为作为锁的对象与线程是属于不同的实例。
类锁
/**
* synchronized 修饰非静态方法
*/
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (MainActivity.class) {
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
运行结果如下:
可以发现,采用类锁一次只能通过一个。即使采用的是 MainActivity.class 这个类锁。
this 对象锁
/**
* synchronized 修饰非静态方法
*/
private void sync5() {
Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
synchronized (this) {
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
运行结果如下:
可以发现线程 1,2 同时结束,3,4 有先后,原因是 3,4 同属于一个实例。
synchronized 修饰方法
作用范围是整个方法,所以方法中所有的代码都是同步的:
/**
* synchronized 修饰非静态方法
*/
private synchronized void sync5() {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(2000);
Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
复制代码
修饰非静态方法:
对于非静态方法,同一个实例的线程访问会被拦截,非同一实例可以同时访问。 即此时是默认对象锁(this)。
修饰静态方法结果
可以看出来,静态方法默认类锁。
总结
对于静态方法,由于此时对象还未生成,所以只能采用类锁;
只要采用类锁,就会拦截所有线程,只能让一个线程访问。
对于对象锁(this),如果是同一个实例,就会按顺序访问,但是如果是不同实例,就可以同时访问。
如果对象锁跟访问的对象没有关系,那么就会都同时访问。
评论