1: 8种锁类型
1.1: 标准访问ab二个线程,是先打印t1还是t2 ???
public class SyncUnit {
public synchronized void t1() {
System.out.println("t1");
}
public synchronized void t2() {
System.out.println("t2");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit.t2();
}).start();
}
}
1.2: t1方法暂停3秒钟,是先打印t1还是t2 ???
public class SyncUnit {
public synchronized void t1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1");
}
public synchronized void t2() {
System.out.println("t2");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit.t2();
}).start();
}
1.3: 新增一个普通方法hello(),是先打印t1还是hello ????
public class SyncUnit {
public synchronized void t1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1");
}
public synchronized void t2() {
System.out.println("t2");
}
public void hello() {
System.out.println("hello");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit.hello();
}).start();
}
}
1.4: 现在有二个SyncUnit对象,是先打印t1还是t2 ???
public class SyncUnit {
public synchronized void t1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1");
}
public synchronized void t2() {
System.out.println("t2");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
SyncUnit syncUnit1 = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit1.t2();
}).start();
}
1.5: 二个静态同步方法,一个SuncUnit对象,是先打印t1还是t2 ????
public class SyncUnit {
public static synchronized void t1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1");
}
public static synchronized void t2() {
System.out.println("t2");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit.t2();
}).start();
}
}
1.6: 二个静态同步方法,二个SyncUnit对象,是先打印t1还是t2
public class SyncUnit {
public static synchronized void t1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1");
}
public static synchronized void t2() {
System.out.println("t2");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
SyncUnit syncUnit1 = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit1.t2();
}).start();
}
}
1.7: 一个静态同步方法,普通同步方法,一个SyncUnit对象,是先打印t1还是t2
public class SyncUnit {
public static synchronized void t1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1");
}
public synchronized void t2() {
System.out.println("t2");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit.t2();
}).start();
}
}
1.8 一个静态同步方法,普通同步方法,二个SyncUnit对象,是先打印t1还是t2
public class SyncUnit {
public static synchronized void t1(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1");
}
public synchronized void t2() {
System.out.println("t2");
}
public static void main(String[] args) throws Exception{
SyncUnit syncUnit = new SyncUnit();
SyncUnit syncUnit1 = new SyncUnit();
new Thread(() -> {
syncUnit.t1();
}).start();
Thread.sleep(100);
new Thread(() -> {
syncUnit1.t2();
}).start();
}
}
1.9 : 结论
- 1: sync修饰普通方法,那么锁的当前对象,同一个实例是可以有多个对象的,一个对象是一把锁
- 2: sync修改静态方法,那么锁的是当前实例,实例在当前上下文中只有一个
- 3: 针对后面几种情况希望大家好好思考,有什么问题可以评论联系我
2:sync是的底层实现原理
2.1: 同步代码块
public class SyncUnit {
private Object obj = new Object();
public void t1() {
synchronized (obj) {
System.out.println("123");
}
}
}
使用 javap -c SyncUnit.class 查看字节码,底层就是使用的是monitorenter和monitorexit
2.2: 一个monitorenter 一定对应多个 monitorexit 吗??
public class SyncUnit {
private Object obj = new Object();
public void t1() {
synchronized (obj) {
System.out.println("123");
throw new RuntimeException("123");
}
}
}
查看字节码文件,你会发现只有一个monitorexit
2.3 同步方法
public class SyncUnit {
private Object obj = new Object();
public synchronized void t1() {
System.out.println("123");
}
}
查看字节码,同步方法使用的是 ACC_SYNCHRONIZED来修饰的,同步方法也是一样
4: sync锁升级
4.1: 无锁
- 无锁代表着根本就不存在线程之间竞争,方法不需要添加做任何同步
4.2: 偏向锁
- HotSpot的作者经过研究发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。偏向锁就是在这种情况下出现的,它的出现只是为了解决只有在一个线程执行同步时提高性能
4.3: 轻量级锁
- 当存在锁竞争的时候,会通过自旋锁的方式来获取轻量级锁,也就是通过CAS来获取锁
5: 锁消除与锁粗化
5.1: 锁消除: 每次执行t1方法都会创建一个Object对象,我们知道一个对象是一把锁,所以有n个线程同时执行t1方法就会创建n个对象,也就是有n把锁,那么只时候加不加synchronized根本没有区别,这就是锁消除
public class SyncUnit {
public void t1() {
Object obj = new Object();
synchronized (obj){
System.out.println("123");
}
}
}
5.2: 锁粗化,虽然这里加了4个synchornized ,但是其实可以只用一个就可以了,这一系列的连续操作都会对同一个对象反复加锁及解锁。即使没有出现线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗
public class SyncUnit {
private Object obj = new Object();
public void t1() {
synchronized (obj){
System.out.println("1");
}
synchronized (obj){
System.out.println("2");
}
synchronized (obj){
System.out.println("3");
}
synchronized (obj){
System.out.println("4");
}
}
//锁粗化
public void t1() {
synchronized (obj){
System.out.println("1");
System.out.println("2");
System.out.println("3");
System.out.println("4");
}
}