synchronized 其实说起来很简单,就是每一个对象都有一个monitor对象。在运行到同步方法,或者同步的
方法块的时候,前面加moniterenter,结束的代码后面接moniterexit。很多人都总结了这么三句话。
- 对于普通方法同步,锁是当前实例对象
- 对于静态方法同步,锁是当前类的 Class 对象
- 对于方法块同步,锁是 Synchronized 括号里的对象
但是,我不知道这三句话是怎么来的,为什么会这样?所以提出了以下几个问题
1 Thread1通过同步方法1,访问静态变量A,另外一个线程Thread2 通过同一个变量Obj访问同步方法1,或者同步方法2,访问静态变量,是顺序执行,还是可以并发访问?
上代码:
/**
* 这个实例说明,synchronized并不支持,读写锁的概念,一个线程在读数据的时候,另外一个线程
* 不能进来
*/
public class Demo003 {
//private int count = 0;
private static int count = 0;
/**
* 一个线程先进来,睡2秒,获取monitor,
* 然后假如说另外一个线程可以进来,说明多个读线程,可以共存
* @return
*/
public synchronized String getCount() {
//public synchronized static vo id add() {
System.out.println(Thread.currentThread().getName()+">enter=====");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">getcount="+count);
return new Integer(count).toString();
}
/**
* 一个线程先进来,睡2秒,获取monitor,
* 然后假如说另外一个线程可以进来,说明多个读线程,可以共存
* @return
*/
public synchronized String getCount2() {
//public synchronized static void add() {
System.out.println(Thread.currentThread().getName()+">enter=====");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">getcount="+count);
return new Integer(count).toString();
}
public static void main(String[] args) {
final Demo003 obj = new Demo003();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
obj.getCount(); //1.同一个对象、同一把锁
}
}, "thread1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
//thread1.add(); //1、同一个对象、同一把锁
obj.getCount2();
}
}, "thread2");
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
结果是顺序执行。 可见synchronized 无法实现juc的读写锁概念。
2 假如说我线程Thread1,锁住class对象,然后另外一个线程Thread2,能获得class对象而且执行class对象的方法吗?
上代码:
/**
* 如果一个线程锁住class对象,能通过实例访问class对象吗?
*/
public class Demo008 {
public void getCount() {
synchronized(Demo008.class){
System.out.println(Thread.currentThread().getName()+">enter=");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">leave=");
}
}
public static void main(String[] args) {
final Demo008 obj = new Demo008();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
obj.getCount(); //1.同一个对象、同一把锁
}
}, "thread1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("获取class对象并且调用class对象的方法");
System.out.println(obj.getClass().getName());
}
}, "thread2");
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
结果是锁住class对象,另外的线程可以拿到class对象,并且调用class的方法
3 假如线程Thread1 访问静态同步方法,另外一个线程Thread2,可以访问静态变量吗?
上代码:
/**
* 这个实例说明,一个线程访问对象的静态同步方法1,另外一个线程通过同一个对象访问静态变量
* 可以进来
* 实际结果: System.out.println(obj.count2);//1、通过对象访问静态变量
* System.out.println(count2);//1、直接访问静态变量
* 都是可以的
* 可以发现带static的同步方法是锁住class对象,如果是不带static是锁住对象
*/
public class Demo005 {
private static int count = 0;
public static int count2 = 5;
public synchronized static void getCount() {
System.out.println(Thread.currentThread().getName()+">enter=");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">getCount="+count);
System.out.println(Thread.currentThread().getName()+">leave=");
}
public static void main(String[] args) {
final Demo005 obj = new Demo005();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
obj.getCount(); //1.同一个对象、同一把锁
}
}, "thread1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
//thread1.add();
System.out.println(obj.count2);//1、通过对象访问静态变量
}
}, "thread2");
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
结果是可以,可见锁住class对象和类的静态变量没有关系
。同理锁住对象和类的属性,非同步方法获取属性没有关系
。
4 还有问题,就是通过前面的文章可以知道,即使cpu内部产生中断,Thread2还是拿不到对象锁。cpu在切换线程的时候,时钟会产生中断,那么是不是可以推断,在线程Thread1拿到对象锁,然后发生异常时,Thread2是不是还是拿不到对象锁?
上代码:
/**
* 如果一个线程锁住对象,能通过实例访问静态变量,或者其他变量
*/
public class Demo010 {
private static Integer count = 0;
public static Integer count2 = 2;
private Integer count3 = 3;
public Integer count4 =4;
public Integer getCount3() {
return count3;
}
public static void main(String[] args) {
final Demo010 obj = new Demo010();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
synchronized (obj){
System.out.println(Thread.currentThread().getName()+">enter=");
int k = 10/0;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">leave=");
}
}
}, "thread1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("对象被锁住时,并且发生异常");
System.out.println(obj.getCount3());
System.out.println(obj.count4);
}
}, "thread2");
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.start();
}
}
代码输出结果:
thread1>enter=
Exception in thread "thread1" java.lang.ArithmeticException: / by zero
at com.xjx.jdktest.concurrent.Demo010$1.run(Demo010.java:27)
at java.lang.Thread.run(Thread.java:745)
对象被锁住时,并且发生异常
3
4
结果是,发生异常后,线程会释放对象锁,使得后面的线程可以继续抢占锁。
5 问题5 synchronized是公平锁还是非公平锁?假如是非公平锁怎么防止活锁,也就是锁饥饿呢?
上代码:
import java.util.concurrent.CountDownLatch;
/**
* synchronized 是非公平锁还是公平锁 5个线程一起启动,3个线程调用synchronized方法,还有一个线程睡了1秒之后
* 才开始调用ynchronized方法,还有一个线程直接拿对象锁,拿到之后睡30s然后执行同步方法
*/
public class Demo011 {
private static CountDownLatch ready = new CountDownLatch(1);
private static Integer count =1;
public synchronized void getCount() {
System.out.println(Thread.currentThread().getName()+">enter getCount=");
System.out.println(Thread.currentThread().getName()+">getCount="+count);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">leave getCount=");
}
public static void main(String[] args) {
final Demo011 obj = new Demo011();
for(int i=0;i<3;i++){
new Thread(new DemoThread(ready,obj)).start();
}
new Thread(new DemoThread2(ready,obj),"DemoThread2").start();
new Thread(new DemoThread3(ready,obj),"DemoThread3").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ready.countDown();
}
}
class DemoThread implements Runnable{
CountDownLatch latch ;
Demo011 obj;
DemoThread(CountDownLatch latch,Demo011 obj){
this.latch = latch;
this.obj = obj;
}
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
obj.getCount();
}
}
class DemoThread2 implements Runnable{
CountDownLatch latch ;
Demo011 obj;
DemoThread2(CountDownLatch latch,Demo011 obj){
this.latch = latch;
this.obj = obj;
}
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
obj.getCount();
}
}
class DemoThread3 implements Runnable{
CountDownLatch latch ;
Demo011 obj;
DemoThread3(CountDownLatch latch,Demo011 obj){
this.latch = latch;
this.obj = obj;
}
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println(Thread.currentThread().getName()+">getObj=");
long start = System.currentTimeMillis();
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
obj.getCount();
System.out.println("this thread has owned monitor for "+new Long(System.currentTimeMillis()-start).toString()+" ms");
System.out.println(Thread.currentThread().getName()+">release Obj=");
}
}
}
输出结果:
Thread-0>enter getCount=
Thread-0>getCount=1
Thread-0>leave getCount=
DemoThread2>enter getCount=
DemoThread2>getCount=1
DemoThread2>leave getCount=
DemoThread3>getObj=
DemoThread3>enter getCount=
DemoThread3>getCount=1
DemoThread3>leave getCount=
this thread has owned monitor for 33001 ms
DemoThread3>release Obj=
Thread-2>enter getCount=
Thread-2>getCount=1
Thread-2>leave getCount=
Thread-1>enter getCount=
Thread-1>getCount=1
Thread-1>leave getCount=
结论: synchronized是非公平锁
,而且如果某些线程执行时间特别长的话,会造成锁饥饿
先写到这里吧,要想看更精彩的内容,请点赞关注吧,请听下回分解。
网友评论