一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在Java
里边就是拿到某个同步对象的锁(一个对象只有一把锁);
如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。
取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,
其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。
以上这段话在一个博文中看到,之前也对java中synchronized同步锁机制搞不清楚。
今天闲来无事也写了一个例子记录一下,顺带自己理解一下JAVA锁
机制中静态同步和非静态同步的区别。
例子调用三个方法
第一个:使用单例模式进行访问(其中区别同步和不同步效果)
第二个:非静态同步(其中区别单例模式和非单例模式效果)
第三个:静态同步
废话不多说开始~~~
第一种方式
使用单例模式调用方式
public class ThreadStudyTest4 extends Thread{
private String nameThread;
public ThreadStudyTest4(String name){
this.nameThread = name;
}
public void run() {
Study1.getInstan().methodA(nameThread);
}
public static void main(String[] args) {
ThreadStudyTest4 ta =new ThreadStudyTest4("AAAA");
ThreadStudyTest4 tb =new ThreadStudyTest4("BBBB");
ta.start();
tb.start();
}
}
首先先创建一个类,这个类是多线程要执行的。也就是会争夺锁的
private static Study1 study= null;
public static Study1 getInstan(){
if(study==null){
study = new Study1();
}
return study;
}
这里我使用单例模式创建这个类,然后看下调用的方法:
public void methodA(String name){
System.out.println(name+":线程进来方法了++++++++");
int count =10;
while(count>0){
System.out.println(name+"xunhuan"+count);
count--;
}
}
只是打印每个线程从5-1的过程,那么如果不同步的话结果是:
BBBBxunhuan5
BBBBxunhuan4
AAAAxunhuan5
BBBBxunhuan3
AAAAxunhuan4
BBBBxunhuan2
AAAAxunhuan3
BBBBxunhuan1
AAAAxunhuan2
AAAAxunhuan1
可以看到结果是乱的,我们希望是看到一个线程打印完另一个线程再打印,
那么我在方法上加同步操作也就是synchronized关键字
BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1
AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1
这次是正确了,(这里解释一下为何B先打印,当线程调用start()方法时线程并不是立即执行)。
那么我们开头说到一个线程获得锁后,另一个线程会等待但我们从这个结果中并不能看到等待的过程,
因此我们改造一下代码。
public void methodA(String name){
System.out.println(name+":线程进来方法了++++++++");
synchronized (object) {
System.out.println("测试线程加锁过程++++++");
int count =5;
while(count>0){
System.out.println(name+"xunhuan"+count);
count--;
}
}
}
按照刚才说的如果一个线程先执行进来后执行打印,另一个线程过来发现object被锁着就先等待,
但依然会打印"线程进来方法了+++"这段话表示它在等待
第一个线程执行完成后释放锁,那么我再来看下结果会不是是这样:
AAAA:线程进来方法了++++++++
AAAA测试线程加锁过程++++++
BBBB:线程进来方法了++++++++
AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1
BBBB测试线程加锁过程++++++
BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1
我们可以看到,线程A先执行,获得object锁,然后执行打印,但是这时线程B也过来执行打印但是
发现线程A为执行完毕,就进行等待,等线程A执行完后释放了锁,线程B获得锁开始执行打印。
这个印证了我们起初第一段提到的锁机制。
第二种非静态同步(其中区别单例模式和非单例模式效果),
改造线程调用类,在run()方法中使用new关键字创建对象
public class ThreadStudyTest4 extends Thread{
private String nameThread;
public ThreadStudyTest4(String name){
this.nameThread = name;
}
public void run() {
new Study1().methodA(nameThread);
}
public static void main(String[] args) {
ThreadStudyTest4 ta =new ThreadStudyTest4("AAAA");
ThreadStudyTest4 tb =new ThreadStudyTest4("BBBB");
ta.start();
tb.start();
}
}
// private static Study1 study= null;
// public static Study1 getInstan(){
// if(study==null){
// study = new Study1();
// }
// return study;
// }
public synchronized void methodB(String name){
int count =5;
while(count>0){
System.out.println(name+"xunhuan"+count);
count--;
}
}
这样执行后结果:
AAAAxunhuan5
BBBBxunhuan5
AAAAxunhuan4
BBBBxunhuan4
AAAAxunhuan3
BBBBxunhuan3
AAAAxunhuan2
BBBBxunhuan2
AAAAxunhuan1
BBBBxunhuan1
是乱的,也就是并没有同步成功,为什么呢?先不要着急,我们再来看下单例模式下的效果,
当然还要再改造一下刚才的代码把run()方法中new关键字改成Study1.getInstan()使用单例模式创建类,
同样把被调用类中单例模式的代码放开,再次执行:
AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1
BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1
正确了!!诶这是为什么呢?首先来看synchronized关键字他的原理就是使用了锁,
如果采用method级别的同步,则对象锁即为method所在的对象,
那么我们第一次使用的是非单例模式,也就是使用了new关键字。
那么就是说A线程执行时创建了一个对象(也就是锁),B线程执行时也创建了锁,他们
本来锁就不是一个锁所以实现不了同步,但是我们使用单例模式时线程A和线程B使用的
是同一个对象也就是同一个锁,所以实现了同步成功!
第三种 静态同步
这里我们就不再演示单例模式和非单例模式的效果因为:
如果是静态方法,对象锁即指method所在的Class对象(唯一)
执行线程的类
public class ThreadStudyTest4 extends Thread{
private String nameThread;
public ThreadStudyTest4(String name){
this.nameThread = name;
}
public void run() {
new Study1().methodC(nameThread);
}
public static void main(String[] args) {
ThreadStudyTest4 ta =new ThreadStudyTest4("AAAA");
ThreadStudyTest4 tb =new ThreadStudyTest4("BBBB");
ta.start();
tb.start();
}
}
被调用的类的方法(这里所有都注释掉,只留这一个方法)
public static synchronized void methodC(String name){
int count =5;
while(count>0){
System.out.println(name+"xunhuan"+count);
count--;
}
}
我们运行结果:
BBBBxunhuan5
BBBBxunhuan4
BBBBxunhuan3
BBBBxunhuan2
BBBBxunhuan1
AAAAxunhuan5
AAAAxunhuan4
AAAAxunhuan3
AAAAxunhuan2
AAAAxunhuan1
刚才说了不区分单例模式和非单例模式,这个没有影响效果是一样的。
到这里我就记录完毕了,希望能帮助到别人吧!同样也帮助了我自己O(∩_∩)O~
参考文档:http://blog.csdn.net/yangzhijun_cau/article/details/6432216(这个文章是转的,但没有标明地址所以只能贴上这里了)