同步锁
Monitor
- monitor指与synchronized关联的同步资源所关联的锁
- monitor有一个计数器,初始化为0,如果monitor的计数器为0,则意味着该monitor的lock还没有被获得,某个线程获得之后将立即对该计数器加一,从此该线程就是这个monitor的所有者了
- 如果一个已经拥有该monitor所有权的线程重入(重新调用该资源),monitor的计数器会再次累加
- 如果monitor已经被其他线程所拥有,则其他线程尝试获取该monitor的所有权时,会被陷入阻塞状态知道monitor计数器变为0,才能再次尝试获取对monitor的拥有权
使用synchronized需要注意的问题
-
与monitor关联的对象不能为空
private final Object mutex=null; public void synMethod(){ synchronized(mutex){ //TODO } }
-
synchronized关键字作用域不应该太大
public static class Task implements Runnable{ public synchronized void run(){ //TODO } }
上面的代码对整个线程的执行域也就是run方法都进行了synchronized同步,从而丧失了并发的能力,synchronized关键字应该尽可能的只作用于共享资源的读写作用域
-
不同的minitor企图锁相同的方法
//模拟排队取号 package online.hengtian.Thread; import java.util.Arrays; import java.util.List; class Run implements Runnable{ private int index=1; private final static int MAX=500; private final static Object obj=new Object(); @Override public void run() { synchronized (obj) { while (index <= MAX) { System.out.println(Thread.currentThread() + " 的号码是:" + index++); } } } } public class TicketWindow { public static void main(String[] args){ List<String> windows= Arrays.asList( "一号窗口","二号窗口","三号窗口","四号窗口" ); /*****************错误示例开始*********************************/ windows.stream() .map(t->new Thread(new Run(),t)) .forEach(Thread::start); /*****************错误示例结束*********************************/ windows= Arrays.asList( "一号窗口","二号窗口","三号窗口","四号窗口" ); Runnable r=new Run(); windows.stream() .map(t->new Thread(r,t)) .forEach(Thread::start); } }
在上面所示的错误示例中,构造了四个Runnable实例,从而让每个线程都拥有了自己的monitor,起不到互斥的作用,在错误实例的下面也给出了正确的做法
-
多个锁交叉导致死锁
private final Object READ=new Object(); private final Object WRITE=new Object(); public void read(){ synchronized(READ){ synchronized(WRITED){ //TDDO } } } public void write(){ synchronized(WRITE){ synchronized(READ){ //TDDO } } }
This Monitor和Class Monitor的详细介绍
This Monitor
synchronized关键字修饰的同一个实例对象的不同方法时,线程争抢的是同一个Monitor的lock,也就是this的Monitor,通过代码二可以验证
//代码一
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class MonitorDemo {
public synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public synchronized void Method2(){
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
public static void main(String[] args){
MonitorDemo demo=new MonitorDemo();
new Thread(demo::Method1,"t1").start();
new Thread(demo::Method2,"t2").start();
}
}
//代码二
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class MonitorDemo {
public synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public void Method2(){
synchronized (this) {
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
}
public static void main(String[] args){
MonitorDemo demo=new MonitorDemo();
new Thread(demo::Method1,"t1").start();
new Thread(demo::Method2,"t2").start();
}
}
代码一是采用的同步方法的方式,代码而是用this的monitor,运行后效果一样,都是运行完方法一之后再运行方法二
Class Monitor
//代码一
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class ClassMonitor {
public static synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public static synchronized void Method2(){
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
public static void main(String[] args){
new Thread(ClassMonitor::Method1,"t1").start();
new Thread(ClassMonitor::Method2,"t2").start();
}
}
//代码二
package online.hengtian.Thread;
import java.util.concurrent.TimeUnit;
public class ClassMonitor {
public static synchronized void Method1(){
System.out.println("Now here is method1");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method1 Termined");
}
public static void Method2(){
synchronized(ClassMonitor.class){
System.out.println("Now here is method2");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method2 Termined");
}
}
public static void main(String[] args){
new Thread(ClassMonitor::Method1,"t1").start();
new Thread(ClassMonitor::Method2,"t2").start();
}
}
代码一和二的运行结果一致,都是先运行方法一之后执行方法二,从而可以说明,在同一个类中的静态方法同步化时,竞争的是class的monitor