Synchronized块对象锁的理解(顺便对方法锁也说明一下)

菜鸟原创,转载注明出处:https://blog.csdn.net/yin18827152962/article/details/82780078

我们都知道sychronized对方法锁调用时候是对对象加锁,这样当线程再次使用对象的加锁资源就不需要重新获取锁,这种机制被称为可重入.(个人学习记录,如有问题望大神指正)

案例代码:

package synchronize;

import java.util.concurrent.TimeUnit;

class SynResource {
	private Boolean synLock = new Boolean(false);
	public void test(Thread _this) {
		System.out.println("test");
		run(_this);
	}
	synchronized public void test1(Thread _this) {
		System.out.println("test1");
		run(_this);
	}

	synchronized public void test2(Thread _this) {
		System.out.println("test2");
		run(_this);
	}

	public void testBlockString(Thread _this) {
		synchronized (String.class) {
			System.out.println("testBlockString");
			run(_this);
		}
	}

	public void testBlockDouble(Thread _this) {
		synchronized (Double.class) {
			System.out.println("testBlockDouble");
			run(_this);
		}
	}

	public void testBlockComm(Thread _this) {
		synchronized (synLock) {
			System.out.println("testBlockComm");
			run(_this);
		}
	}
	public void run(Thread _this){
		while (true) {
			try {
				Thread.sleep(TimeUnit.SECONDS.toSeconds(1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("thread name = "+_this.currentThread().getName()+" methodName = "+((ThreadTest)_this).methodName+" 该线程不会结束!");
		}
	}
}

class ThreadTest extends Thread {
    //由jvm保证ThreadTest中syn是单例,保证它是个临界资源
	private static SynResource syn = new SynResource();
	String methodName = "";
	public ThreadTest(String methodName){
		this.methodName = methodName;
	}
	@Override
	public void run() {
		switch (methodName) {
		case "test1":
			syn.test1(this);
			break;
		case "test2":
			syn.test2(this);
			break;
		case "test":
			syn.test(this);
			break;
		case "testBlockString":
			syn.testBlockString(this);
			break;
		case "testBlockDouble":
			syn.testBlockString(this);
			break;
		case "testBlockComm":
			syn.testBlockComm(this);
			break;
		default:
			break;
		}
	}
}

public class SynTest {
	public static void main(String[] args) {
		ThreadTest tt1 = new ThreadTest("test1");
		ThreadTest tt2 = new ThreadTest("test");
		//开启线程
		tt1.start();
		tt2.start();
	}
}

方法锁:

1.调用未加锁的代码线程不会阻塞

代码调用:

public class SynTest {
	public static void main(String[] args) {
		ThreadTest tt1 = new ThreadTest("test");
		ThreadTest tt2 = new ThreadTest("test");
		//开启线程
		tt1.start();
		tt2.start();
	}
}

输出结果:

Synchronized块对象锁的理解(顺便对方法锁也说明一下)_第1张图片

结论:两个线程可以安然无恙执行(很正常,简直废话)

2.调用加方法锁和不加方法锁的资源

代码调用:

public class SynTest {
	public static void main(String[] args) {
		ThreadTest tt1 = new ThreadTest("test1");
		ThreadTest tt2 = new ThreadTest("test");
		//开启线程
		tt1.start();
		tt2.start();
	}
}

输出结果:

Synchronized块对象锁的理解(顺便对方法锁也说明一下)_第2张图片

结论:方法锁锁定对象后,只会对需要同步的方法加锁,需要同步的方法可以正常的被执行,即虽然锁了对象,但是不是对对象所有的资源进行加锁,废话一句,所以成员的私有化就很有必要,可以更好保证准确同步.

3.调用两个不同的同步方法

代码调用:

public class SynTest {
	public static void main(String[] args) {
		ThreadTest tt1 = new ThreadTest("test1");
		ThreadTest tt2 = new ThreadTest("test2");
		//开启线程
		tt1.start();
		tt2.start();
	}
}

输出结果:

Synchronized块对象锁的理解(顺便对方法锁也说明一下)_第3张图片

结论:方法锁锁定是对象(及该类的对象,强调这点将有利于理解后面的块级的对象加锁),即只要调用了一个同步方法,其他线程(!!!!,自己线程是可以重入调用其他同步方法,不需要重新获取锁)将不能调用任何同步方法

块级锁

1.块级锁和普通方法不会产生竞争(测试略)

2.锁同一个对象会产生竞争,很容易理解

代码调用:

public class SynTest {
	public static void main(String[] args) {
		ThreadTest tt1 = new ThreadTest("testBlockDouble");
		ThreadTest tt2 = new ThreadTest("testBlockDouble");
		//开启线程
		tt1.start();
		tt2.start();
	}
}

输出结果:

Synchronized块对象锁的理解(顺便对方法锁也说明一下)_第4张图片

结论:对象调用使用同一个对象锁会产生阻塞,不阻塞才怪,但是这里还不能开出来,块级锁锁的不是当前被调用方法的对象

 

3.锁不同对象

代码调用:

public class SynTest {
	public static void main(String[] args) {
		ThreadTest tt1 = new ThreadTest("testBlockDouble");
		ThreadTest tt2 = new ThreadTest("testBlockString");
		//开启线程
		tt1.start();
		tt2.start();
	}
}

输出结果:

Synchronized块对象锁的理解(顺便对方法锁也说明一下)_第5张图片

结论:其实块级锁锁的已经不是当对象,而是我们synchronized()括号中的对象(简直就是废话一样),仅仅从实现讲,我们完全可以使用这种方法让几个相关联的方法使用通另一对象,然后一起加锁,对另一类使用另一个对象进行加锁,不知道又没有这样的需求,我也就是自己瞎想的

4.调用new出来的公共对象, 和使用同一个class对象一样,不再啰嗦了

Synchronized块对象锁的理解(顺便对方法锁也说明一下)_第6张图片

最后瞎总结:看似方法锁和块级锁一个是锁对象,一个是锁区域,其实二者十一摸一样的,都是锁对象的,我们完全可以理解为方法锁的synchronized后面虚拟机自动添加了(this)部分,所以我们可以这样来看同步问题分为三个部分:

线程

资源

钥匙(就是我们加锁使用的对象)

本文章表精髓所在:那个线程抢到了要钥匙就可以执行哪些代码(非同步的都可以执行),线程拥有了钥匙,该钥匙对象也就也会被加锁,不能被其他线程调用同步方.即此时资源和钥匙都被线程锁定,然而外界线程往往不需要改钥匙对象,使用synchronized方法同步方式,我们的资源对象和钥匙对象就是同一个对象(我感觉我这样理解很对,也很有新意)

修改代码测试,重新粘贴实例代码全部:

package synchronize;

import java.util.concurrent.TimeUnit;

class YaoShi {
	synchronized public void test1() {
		String str = "钥匙中的同步方法是否会被同步?";
		run(str);
	}

	public void test2() {
		String str = "这是钥匙中的非同步方法";
		run(str);
	}
	
	public void run(String str) {
		while (true) {
			try {
				Thread.sleep(TimeUnit.SECONDS.toSeconds(1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(str);
		}
	}
}

class SynResource {
	// 本程序调用中SynResource是单例的,所以synLock
	private static YaoShi synLock = new YaoShi();
	public static YaoShi getSynLock(){
		return synLock;
	}
	public void test(Thread _this) {
		System.out.println("test");
		run(_this);
	}

	synchronized public void test1(Thread _this) {
		System.out.println("test1");
		run(_this);
	}

	synchronized public void test2(Thread _this) {
		System.out.println("test2");
		run(_this);
	}

	public void testBlockString(Thread _this) {
		synchronized (String.class) {
			System.out.println("testBlockString");
			run(_this);
		}
	}

	public void testBlockDouble(Thread _this) {
		synchronized (Double.class) {
			System.out.println("testBlockDouble");
			run(_this);
		}
	}

	public void testBlockComm(Thread _this) {
		synchronized (synLock) {
			System.out.println("testBlockComm");
			run(_this);
		}
	}

	public void run(Thread _this) {
		while (true) {
			try {
				Thread.sleep(TimeUnit.SECONDS.toSeconds(1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("thread name = " + _this.currentThread().getName() + " methodName = "
					+ ((ThreadTest) _this).methodName + " 该线程不会结束!");
		}
	}

	public void testOtherObj(ThreadTest _this) {
		// 被加锁的对象当成了钥匙
		synchronized (synLock) {
			System.out.println("testOtherObj");
			run(_this);
		}
	}
}

class ThreadTest extends Thread {
	// 由jvm保证ThreadTest中syn是单例,保证它是个临界资源
	private static SynResource syn = new SynResource();
	String methodName = "";

	public ThreadTest(String methodName) {
		this.methodName = methodName;
	}

	@Override
	public void run() {
		switch (methodName) {
		case "test1":
			syn.test1(this);
			break;
		case "test2":
			syn.test2(this);
			break;
		case "test":
			syn.test(this);
			break;
		case "testBlockString":
			syn.testBlockString(this);
			break;
		case "testBlockDouble":
			syn.testBlockDouble(this);
			break;
		case "testBlockComm":
			syn.testBlockComm(this);
			break;
		case "testOtherObj":
			syn.testOtherObj(this);
			break;
		default:
			break;
		}
	}
	public static SynResource getSyn(){
		return syn;
	}
}

public class SynTest {
	public static void main(String[] args) throws InterruptedException {
		ThreadTest tt1 = new ThreadTest("testOtherObj");//试图通过这种方法获取钥匙
		Thread t = new Thread(new Runnable() {
			
			@Override
			public void run() {
				ThreadTest.getSyn().getSynLock().test1();//调用钥匙.看看是否阻塞
				//new YaoShi().test1();//注意这是新创建的要是,不是加锁的钥匙
			}
		});
		// 开启线程
		t.start();
		Thread.sleep(1000);
		tt1.start();
	
	}
}

结果:要是代码被资源代码阻塞

Synchronized块对象锁的理解(顺便对方法锁也说明一下)_第7张图片

如有问题,望大神批评指正

转载注明出处:https://blog.csdn.net/yin18827152962/article/details/82780078

 

你可能感兴趣的:(多线程,并发)