1、 synchronized关键字使用在方法上面,代码如下
@Slf4j(topic = "test")
public class BasicLock {
public synchronized void x(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("x");
}
public synchronized void y(){
log.debug("y");
}
public void z(){
log.debug("z");
}
}
测试类
@Slf4j(topic = "test")
public class TestBasic2 {
public static void main(String[] args) {
BasicLock basicLock = new BasicLock();
new Thread(()->{
log.debug("start");
basicLock.x();
},"t1").start();
new Thread(()->{
log.debug("start");
basicLock.y();
},"t2").start();
new Thread(()->{
log.debug("start");
basicLock.z();
},"t3").start();
}
}
打印结果如下
第一次执行
16:37:26.219 [t1] DEBUG test - start
16:37:26.219 [t2] DEBUG test - start
16:37:26.219 [t3] DEBUG test - start
16:37:26.224 [t3] DEBUG test - z
16:37:27.224 [t1] DEBUG test - x
16:37:27.224 [t2] DEBUG test - y
第二次执行
16:38:51.772 [t3] DEBUG test - start
16:38:51.772 [t2] DEBUG test - start
16:38:51.772 [t1] DEBUG test - start
16:38:51.777 [t3] DEBUG test - z
16:38:51.777 [t2] DEBUG test - y
16:38:52.777 [t1] DEBUG test - x
第三次执行
16:39:40.283 [t2] DEBUG test - start
16:39:40.283 [t1] DEBUG test - start
16:39:40.283 [t3] DEBUG test - start
16:39:40.287 [t2] DEBUG test - y
16:39:40.287 [t3] DEBUG test - z
16:39:41.288 [t1] DEBUG test - x
三个线程几乎同时启动,由于 x和y方法使用了synchronized 关键字加锁,所以 x和y的打印始终是间隔1秒,而z没有加synchronized关键字进行加锁,所以打印肯定不会超过1秒
2、synchronized 对两个对象进行加锁
新增一个类
@Slf4j(topic = "test") public class BasicLock1 { public synchronized static void x(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("x"); } public synchronized void y(){ log.debug("y"); } public void z(){ log.debug("z"); } }
测试类
@Slf4j(topic = "test") public class TestBasic3 { public static void main(String[] args) { BasicLock basicLock = new BasicLock(); BasicLock basicLock1 = new BasicLock(); new Thread(()->{ log.debug("start"); basicLock.x(); },"t1").start(); new Thread(()->{ log.debug("start"); basicLock1.y(); },"t2").start(); } }
测试类中 进行加锁的是两个对象,所以两个线程的执行互不影响
打印如下
16:51:59.416 [t2] DEBUG test - start
16:51:59.416 [t1] DEBUG test - start
16:51:59.421 [t2] DEBUG test - y
16:52:00.422 [t1] DEBUG test - x
3、synchronized 对静态方法加锁和对普通方法加锁的区别
测试类(锁对象继续使用上文中的 BasicLock1)
@Slf4j(topic = "test") public class TestBasic4 { public static void main(String[] args) { BasicLock1 basicLock1 = new BasicLock1(); new Thread(()->{ log.debug("start"); basicLock1.x(); },"t1").start(); new Thread(()->{ log.debug("start"); basicLock1.y(); },"t2").start(); } }
打印结果
16:53:03.465 [t1] DEBUG test - start
16:53:03.465 [t2] DEBUG test - start
16:53:03.469 [t2] DEBUG test - y
16:53:04.470 [t1] DEBUG test - x
4、synchronized 对同一个类的不同的静态方法加锁
@Slf4j(topic = "test") public class BasicLock2 { public synchronized static void x(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("x"); } public synchronized static void y(){ log.debug("y"); } public void z(){ log.debug("z"); } }
测试类
@Slf4j(topic = "test") public class TestBasic5 { public static void main(String[] args) { BasicLock2 basicLock2 = new BasicLock2(); new Thread(()->{ log.debug("start"); basicLock2.x(); },"t1").start(); new Thread(()->{ log.debug("start"); basicLock2.y(); },"t2").start(); } }
打印如下
16:56:27.380 [t2] DEBUG test - start
16:56:27.380 [t1] DEBUG test - start
16:56:27.384 [t2] DEBUG test - y
16:56:28.385 [t1] DEBUG test - x
或
16:56:40.872 [t1] DEBUG test - start
16:56:40.872 [t2] DEBUG test - start
16:56:41.875 [t1] DEBUG test - x
16:56:41.875 [t2] DEBUG test - y
总结如下
1、
/** * synchronized关键字 * synchronized关键字锁定的是对象不是代码块,demo中锁的是object对象的实例 * 锁定的对象有两种:1.类的实例 2.类对象(类锁) * 加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一。 */ @Slf4j(topic = "test") public class Demo1 { private int count = 10; private Object object = new Object(); public void test(){ synchronized (object){ count--; log.debug(Thread.currentThread().getName() + " count = " + count); } } }
2、
@Slf4j(topic = "test") public class Demo2 { private int count = 10; public void test(){ //synchronized(this)锁定的是当前类的实例,这里锁定的是Demo2类的实例 synchronized (this){ count--; log.debug(Thread.currentThread().getName() + " count = " + count); } } }
3、
@Slf4j(topic = "test") public class Demo3 { private int count = 10; //直接加在方法声明上,相当于是synchronized(this) public synchronized void test(){ count--; log.debug(Thread.currentThread().getName() + " count = " + count); } }
4、
@Slf4j(topic = "test") public class Demo4 { private static int count = 10; //synchronize关键字修饰静态方法锁定的是类的对象 public synchronized static void test(){ count--; log.debug(Thread.currentThread().getName() + " count = " + count); } public static void test2(){ synchronized (Demo4.class){//这里不能替换成this count--; } } }
synchronized 关键字使用应注意的地方
1、
** * 锁对象的改变 * 锁定某对象o,如果o的属性发生改变,不影响锁的使用 * 但是如果o变成另外一个对象,则锁定的对象发生改变 * 应该避免将锁定对象的引用变成另外一个对象 */ @Slf4j(topic = "test") public class Demo1 { Object o = new Object(); public void test(){ synchronized (o) { //t1 在这里无线执行 while (true) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } log.debug(Thread.currentThread().getName()); } } } public static void main(String[] args) { Demo1 demo = new Demo1(); new Thread(demo :: test, "t1").start(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } Thread t2 = new Thread(demo :: test, "t2"); demo.o = new Object(); //t2能否执行? t2.start(); } }
2、
/** * 不要以字符串常量作为锁定的对象 * */ @Slf4j(topic = "enjoy") public class Demo2 { String s1 = "hello"; String s2 = "hello"; public void test1(){ synchronized (s1) { log.debug("t1 start..."); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("t1 end..."); } } public void test2(){ synchronized (s2) { log.debug("t2 start..."); } } public static void main(String[] args) { Demo2 demo = new Demo2(); //啓動t1 new Thread(demo :: test1,"t1").start(); //启动t2 new Thread(demo :: test2,"t2").start(); } }
3、
/** * 同步代码快中的语句越少越好 * 比较test1和test2 * 业务逻辑中只有count++这句需要sync,这时不应该给整个方法上锁 * 采用细粒度的锁,可以使线程争用时间变短,从而提高效率 */ @Slf4j(topic = "enjoy") public class Demo3 { int count = 0; public synchronized void test1(){ try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } count ++; try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 局部加锁 */ public void test2(){ try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (this) { count ++; } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } }