1、同步方法和非同步方法的先后调用顺序
同步方法和非同步方法可不可以同时调用?
package com.example.demo.gc7;
public class T {
public synchronized void m1(){
System.out.println(Thread.currentThread().getName()+"m1 start");
try {
Thread.sleep(10000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"m1 end");
}
public void m2(){
try{
Thread.sleep(10000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"m2");
}
public static void main(String[] args) {
T t = new T();
/* new Thread(() ->
t.m2(),"t2"
).start();
new Thread(() ->
t.m1(),"t1").start();*/
new Thread(t :: m1,"t1").start();
new Thread(t :: m2,"t2").start();
}
}
答案肯定是可以的。因为在访问m1同步方法时候。加锁。但是我想访问m2的时候并不需要加锁。所以可以同时访问
t :: m1 等同于t.m1();
2、dirtyRead脏读问题
其实就是。加锁的方法和未加锁方法同时运行。也就是上面的同步方法和非同步方法的同时调用
####面试题:
如果有这么一种场景:一个账户。一个姓名和余额。写方法给它设置金额。读方法通过姓名读取余额、这种业务场景会不会出现问题?
package com.example.demo.gc8;
import java.util.concurrent.TimeUnit;
public class Account {
String name;
double balance;
public synchronized void set(String name,double balance){
try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
this.balance = balance;
}
public /*synchronized*/ double getBalance(String name){
return this.balance;
}
public static void main(String[] args) {
Account a = new Account();
new Thread(() -> a.set("zhangsan",100.0)).start();
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(a.getBalance("zhangsan"));
}
}
(1)看你业务需求。set方法是同步方法。当线程睡眠时候。如果下面getbalace方法不是同步的。将会出现脏读情况。如果业务允许。则可以不加synchronized
(2)两个同步方法不会出现脏读情况。但是加锁以后效率低下。
3、synchronized可重入锁
可重入锁发生在两个同步方法中
一个线程。两个同步方法。可以同时访问。这就是synchronized可重入锁
保证同一线程,才能获得锁资源
package com.example.demo.gc9;
import java.util.concurrent.TimeUnit;
public class T {
// 可重入锁
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() {
try{
TimeUnit.SECONDS.sleep(2);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("m2");
}
public static void main(String[] args) {
new T().m1();
}
}
模拟父类子类
子类重写父类同步方法。也是加可重入锁
package com.example.demo.gc10;
import java.util.concurrent.TimeUnit;
public class T {
synchronized void m(){
System.out.println("m start");
try {
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("m end");
}
public static void main(String[] args) {
new TT().m();
}
}
class TT extends T{
@Override
synchronized void m(){
System.out.println("child m start");
super.m();
System.out.println("child m end");
}
}
4、synchronized异常锁
异常锁就是指定某种条件下。。。被想拿到锁资源的线程乱冲进来。发生的异常
package com.example.demo.gc11;
import java.util.concurrent.TimeUnit;
public class T {
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(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 5){
int i = 1/0; //此处自定义算数异常
System.out.println(i);
}
}
}
public static void main(String[] args) {
T t =new T();
Runnable r = new Runnable() {
@Override
public void run() {
t.m();
}
};
new Thread(r,"t1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(r,"t2").start();
}
}
###发生异常后。另外线程抢到锁资源的一种过程
5、synchronized底层
synchronized底层就是锁升级。
何为锁升级。能用轻量级锁。则用轻量级锁。锁不住,则往上用
一个线程访问,并没有对对象加锁时候。只是记录这个线程的id:偏向锁
偏向锁如果有线程争用。则升级为自旋锁
自旋锁。旋十次以后升级为重量级锁
参考资料
synchronized
执行时间短。线程少。用自旋锁。
执行时间长。用系统锁