java多线程对象锁、类锁、同步机制详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shuaipu813/article/details/53582468

1.在java多线程编程中对象锁、类锁、同步机制synchronized详解:

    对象锁:在java中每个对象都有一个唯一的锁,对象锁用于对象实例方法或者一个对象实例上面的。

    类锁:是用于一个类静态方法或者class对象的,一个类的实例对象可以有多个,但是只有一个class对象。

    同步机制synchronizedsynchronized关键字用于修饰方法或者单独的synchronized代码块,当一个线程想执行synchronized中的内容时,必须先获取到对象锁,当对象锁没有线程占用时,进入synchronized方法会自动获取到对象锁,执行完毕后会自动释放锁,如果对象锁被A线程占用,B线程想执行synchronized的代码只能等待A个线程执行完毕后,释放对象锁,B线程才能获取到对象锁进入方法执行。一个线程获得对象A的锁,也可以获得对象B的锁,两个不同类的对象锁没有关联。

举例说明包含synchronized的方法和synchronized代码块

package com.test;

public class TestThread {

	public void test1() {
		synchronized (this) {
			int i = 0;
			while (i++<5) {
				System.out.println(Thread.currentThread().getName() + " : " + i);
				try {
					Thread.sleep(500);
				} catch (InterruptedException ie) {
				}
			}
		}
	}

	public synchronized void test2() {
		int i = 0;
				while (i++<5) {
			System.out.println(Thread.currentThread().getName() + " : " + i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException ie) {
			}
		}
	}

	public static void main(String[] args) {
		final TestThread test = new TestThread();
		Thread test1 = new Thread(new Runnable() {
			public void run() {
				test.test1();
			}
		}, "test1");
		Thread test2 = new Thread(new Runnable() {
			public void run() {
				test.test2();
			}
		}, "test2");
		test1.start();
		test2.start();
	}
}


以上代码执行结果如下:

以上代码是因为执行同一个对象,所以另一个线程要等前一个对象,执行完成后释放掉对象锁,拿到对象锁才能继续执行synchronized里面的东西。

如果我们去掉synchronized修饰的方法或者synchronized代码块,将会打印出以下的结果:


如何要是去掉一个synchronized后,输出的语句是交叉执行的。这就说明,对于同一个对象,如果线程A得到了对象锁,线程B可以访问对象没有同步的方法和代码。进行同步的代码和没有同步的代码是互不影响的。

举例类锁:

package com.test;
public class MyThreadClass {
	public static void main(String[] args) {
		final MyThreadClass my=new MyThreadClass();
		Thread thread1=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					my.test1();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test1");
		Thread thread2=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					MyThreadClass.test2();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test2");
		thread1.start();
		thread2.start();
	}
	
	
	public void test1() throws InterruptedException{
		synchronized (MyThreadClass.class) {
			int i=0;
			while(i++<5){
				System.out.println(Thread.currentThread().getName()+":"+i);
				Thread.sleep(500);
			}
		}
	}
	
	public static synchronized void test2() throws InterruptedException{
			int i=0;
			while(i++<5){
				System.out.println(Thread.currentThread().getName()+":"+i);
				Thread.sleep(500);
			}
	}
}	
执行结果:



类锁和对象锁其实是一样的,只是针对于不同的对象。

如果两个方法一个被synchronized修饰,一个静态方法被synchronized修饰(体现类锁和对象锁的区别),代码如下:

package com.test;
public class MyThreadClass2 {
	public static void main(String[] args) {
		final MyThreadClass2 my=new MyThreadClass2();
		Thread thread1=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					my.test1();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test1");
		Thread thread2=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					MyThreadClass2.test2();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test2");
		thread1.start();
		thread2.start();
	}
	
	public synchronized void test1() throws InterruptedException{
			int i=0;
			while(i++<5){
				System.out.println(Thread.currentThread().getName()+":"+i);
				Thread.sleep(500);
			}
	}
	
	public static synchronized void test2() throws InterruptedException{
			int i=0;
			while(i++<5){
				System.out.println(Thread.currentThread().getName()+":"+i);
				Thread.sleep(500);
			}
	}
}	
代码执行结果如下:


结果也是交叉输出的,虽然都是被synchronized修饰,但是一个是属于对象的,一个是属于class。所以类锁和对象锁是不同的,他们控制着不同的区域,互不干扰。

疑问:java中既然synchronized修饰的方法和synchronized代码块作用是一样的,为什么还需要synchronized代码块呢?

使用synchronized修饰方式是直接在方法上面加锁,synchronized方法块是在方法里面加锁,一个范围大,一个范围小。还有一个最主要的在应用场景如下:在Class中创建一个对象,在Class中要执行这个对象的某个方法,为了防止多个线程同时执行,采用同步加锁,但是如果这个方法出现死循环或者执行时间很长,其他线程也不能执行对象的其他同步方法,需要等待这个线程执行完毕,影响系统性能。如果采用synchronized修饰方法和synchronized代码块可能都会出现这种情况,我们来模拟一下这种状况:

创建一个java对象类,里面有两个方法:

package com.test;

public class Test {
	public void test1(){
	}
	public void test2(){
	}
}
创建测试类代码如下:

package com.test;

public class MyThreadClass4 {
	static Test test=new Test();
	public static void main(String[] args) {
		final MyThreadClass4 my=new MyThreadClass4();
		Thread thread1=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					my.test1();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test1");
		Thread thread2=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					my.test2();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test2");
		thread1.start();
		thread2.start();
	}
	long startTime;
	public synchronized void test1() throws InterruptedException{
			test.test1();
			System.out.println(Thread.currentThread().getName());
			startTime = System.currentTimeMillis();//获取当前时间
			Thread.sleep(5000);
	}
	
	public synchronized void test2() throws InterruptedException{
		long endTime = System.currentTimeMillis();
		test.test2();
		System.out.println(Thread.currentThread().getName());
		System.out.println("程序运行时间:"+(endTime-startTime)+"ms");
	}
}	

在class里面创建对象实例,然后将两个方法分别调用,在调用方法上面都加上synchronized修饰方法Thread.sleep(5000)来模拟方法执行时间过长或者死循环,但是可以看到时间为我们设置的时间,执行结果不理想执行结果如下:


如果我们采用synchronized代码块的方式如下:

package com.test;

public class MyThreadClass3 {
	static Test test=new Test();
	public static void main(String[] args) {
		final MyThreadClass3 my=new MyThreadClass3();
		Thread thread1=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					my.test1();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test1");
		Thread thread2=new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					my.test2();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"test2");
		thread1.start();
		thread2.start();
	}
	long startTime;
	public void test1() throws InterruptedException{
		synchronized (test) {
			test.test1();
			System.out.println(Thread.currentThread().getName());
			startTime = System.currentTimeMillis();//获取当前时间
			Thread.sleep(5000);
		}
	}
	
	public synchronized void test2() throws InterruptedException{
		long endTime = System.currentTimeMillis();
		test.test2();
		System.out.println(Thread.currentThread().getName());
		System.out.println("程序运行时间:"+(endTime-startTime)+"ms");
	}
}	
synchronized代码块中我们只是对当前test对象加锁,和执行这块代码的对象没有任何关系。执行test1的同时,我照样可以执行其他的synchronized同步方法,增强系统性能。执行结果如下:

猜你喜欢

转载自blog.csdn.net/shuaipu813/article/details/53582468