synchronized底层实现:
在JDK早期是重量级的;
后来改进—>锁升级的概念;不可逆只能升级不能降级
markWord记录这个线程的ID(偏向锁);
如果线程争用:升级---->自旋锁(占用cpu)适用于时间段、数量少的线程;
自旋锁触发10次以后升级为重量级锁---->os(不占用cpu);
执行时间短,线程数少用自旋锁;
执行时间长,线程数多用系统锁。
1、程序执行过程中,如果出现异常,默认情况下锁被释放
- 所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。
- 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适,
- 在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。
- 因此要非常小心的处理同步业务逻辑中的异常
public class Thread_002_SynchronizedException {
int count = 0;
synchronized void m() {
System.out.println(Thread.currentThread().getName() + " start");
while (true) {
count++;
System.out.println(Thread.currentThread().getName() + " count = " + count);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 异常情况
if(count == 5) {
int i = count / 0;
System.out.println(i);
}
}
}
public static void main(String[] args) {
Thread_002_SynchronizedException demoSynchronizedException001 =
new Thread_002_SynchronizedException();
Runnable runnable = new Runnable() {
@Override
public void run() {
demoSynchronizedException001.m();
}
};
new Thread(runnable, "t1").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(runnable, "t2").start();
}
}
运行结果:
2、synchronized可重入锁
一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.
public class Thread_003_Synchronized {
synchronized void m1() {
System.out.println("m1----start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1----end");
}
synchronized void m2() {
System.out.println("m2----start");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2----end");
}
public static void main(String[] args) {
new Thread_003_Synchronized().m1();
}
}
运行结果:
这里是继承中有可能发生的情形,子类调用父类的同步方法
public class Thread_004_Synchronized {
synchronized void m() {
System.out.println("parent m start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("parent m end");
}
public static void main(String[] args) {
new ChildDemoSynchronized002().m();
}
}
class ChildDemoSynchronized002 extends Thread_004_Synchronized {
@Override
synchronized void m() {
System.out.println("child m start");
super.m();
System.out.println("child m end");
}
}
运行结果:
3、面试题:模拟银行账户
对业务写方法加锁,业务读方法不加锁这样行不行?
答案:看具体业务需求,容易产生脏读问题(dirtyRead)
银行账户肯定不允许脏读
public class Thread_005_Acount {
private String name;
private double balance;
public synchronized void set(String name, double balance) {
this.name = name;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.balance = balance;
}
public /*synchronized*/ double getBalance(String name) {
return this.balance = balance;
}
public static void main(String[] args) {
Thread_005_Acount dat = new Thread_005_Acount();
new Thread(()->dat.set("lisi", 1000.00)).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("查询余额为:" + dat.getBalance("lisi"));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("查询余额为:" + dat.getBalance("lisi"));
}
}
运行结果: