文章目录
synchronized锁定的资源
synchronized
修饰的是方法或者代码块来实现同步,但其实锁定的资源其实是对象。synchronized
修饰于3种方式(静态方法、普通方法、方法块),其锁定的资源有2种(类对象、类的实例)加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一。
synchronized
修饰静态方法,锁定的资源是Class对象(类对象,Class class = x x x.Class)
两种方式:
public class Demo {
private static int count = 10;
//synchronize关键字修饰静态方法锁定的是类对象
//方式1
public synchronized static void test(){
//临界区
count--;
}
//方式二
public static void test2(){
synchronized (Demo4.class){
//这里不能替换成this
//临界区
count--;
}
}
}
synchronized
修饰普通方法,锁定的资源是类的实例(User o = new User())
三种方式:
- 对象用new的方式
private int count = 10;
private Demo reentryDemo = new Demo();
public void test(){
synchronized (object){
//临界区
count--;
}
}
- 用this来代替,当前对象的实例
public void test() {
synchronize (this) {
}
}
- 直接修饰在普通方法上
public synchronized void test() {
}
synchronized
修饰代码块/方法块,其实实际上也是方法的一部分, 所以锁定的资源也是根据方法来。
锁定对象改变
-
如果锁定对象的属性发生改变,不会影响锁的使用。
-
如果锁定对象修改为另外一个对象,这种锁定对象的改变了,等于是一把新锁了。
示例代码:
public class Demo {
public Lock lock = new Lock();
public void test () {
synchronized (lock) {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("hello word");
}
}
}
public static void main(String[] args) throws InterruptedException {
Demo reentryDemo = new Demo();
new Thread(reentryDemo::test,"t1").start();
TimeUnit.MILLISECONDS.sleep(10);
// reentryDemo.lock.i=20;
reentryDemo.lock = new Lock();
new Thread(reentryDemo::test,"t2").start();
}
}
class Lock {
public int i = 0;
}
锁定对象为字符串常量
不要以字符串常量作为锁定的对象,在jvm中两个相同的字符串都是指向常量池同一块内存。
所以虽然名字不同,但实际上还是同一把锁。
public String sLock1 = "lock";
public String sLock2 = "lock";
public void test1 () {
synchronized (sLock1) {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("hello word");
}
}
}
public void test2() {
synchronized (sLock2) {
log.debug("test2 end");
}
}
另外一种情况,不属于并发的知识点了。这种就是2个对象了。
public String sLock1 = new String("lock");
public String sLock2 = new String("lock");
减小锁的颗粒度
通过减少同步的代码块,降低锁的颗粒度。同步的代码块越少越好,从而提高效率。
比如说在业务上只要保证count的同步,可以采用细颗粒度的锁,从而减少线程竞争的时间。
优化方案:就只需要对count++局部上锁,不用对整个方法上锁。
Bad:
int count = 0;
public synchronized void test(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
count ++;
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace(