JAVA-锁-线程

该文档不讨论同一同步块在不同线程的使用,而讨论其底层的锁机制,不同的同步块之间会有何种影响,这种影响不可避免的降低性能

1. 共享实例都会发生争用

2. 自有实例都不会发生争用,除以下情况:

         2.1. 加锁对象为直接使用的字符串,如“abc”。因为字符串这样使用,实在常量池中创建,不管如何使用,都只创建一次

         2.2. 加锁对象被static修饰。static修饰都归类所有,不属于实例。

                     2.2.1 static修饰方法。synchronized修饰方法,加锁对象为该实例,static修饰的方法不属于实例,属于类,synchronized修饰该方法时加锁对象则变成该类的monitor

                     2.2.2 加锁对象是对象,该对象被static修饰

         

1. synchronized关键字修饰方法时,加锁对象为实例或类
1.1自有实例(LockTest.java)无争用
public class LockTest {
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest().printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest().printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	public synchronized void printString(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printString "+i);
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	public synchronized void printDate(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printDate "+new Date().toLocaleString());
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}

结论:

打印结果可看出两线程同时在执行


JProfiler没有监测到Monitor&Lock信息,也没观察到线程阻塞



1.2 共享实例,LockTest2.java 总有一个被锁住,两个线程只能顺序执行

代码:

public class LockTest2 {	
	public static void main(String[] args) {
		final LockTest2 instance = new LockTest2();
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					instance.printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					instance.printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}	

	public synchronized void printString(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printString "+i);
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	

	public synchronized void printDate(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printDate "+new Date().toLocaleString());
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}

结论:

打印结果可看出执行是有顺序的,两个线程互斥


JProfiler工具可以看到,有线程阻塞(红色部分)


两个线程(String thread, Date thread)在不同时间段都有阻塞


“Current Locking Graph”视图,一旦有线程锁住,主视图将有以下画面,两个线程正在争用同一个monitor,红色箭头表示线程阻塞,等待锁释放,黑色箭头表示获得锁,正在执行


1.3 静态方法,自有实例(LockTest3.java)发生争用现象

代码:

public class LockTest3 {
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest3().printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest3().printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	

	public static synchronized void printString(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printString "+i);
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	

	public static synchronized void printDate(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printDate "+new Date().toLocaleString());
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


public class LockTest4 {	
	public static void main(String[] args) {
		final LockTest4 instance = new LockTest4();
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					instance.printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					instance.printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	public static synchronized void printString(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printString "+i);
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	public static synchronized void printDate(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printDate "+new Date().toLocaleString());
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

public class LockTest5 {
	
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					LockTest5.printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					LockTest5.printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	public static synchronized void printString(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printString "+i);
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
	public static synchronized void printDate(){
		for (int i = 0; i < 3; i++) {
			System.out.println("printDate "+new Date().toLocaleString());
			try {
				Thread.currentThread().sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

结论:

结果是顺序打印,线程互斥,静态方法的共享实例则更确定会发生争用(LockTest4.java)

其实被static修饰的字段和方法都属类所有,通过类可以直接调用(LockTest5.java)。使用synchronized同步,其加锁对象是同一个,与实例无关,也不需通过实例调用。LockTest3,LockTest4通过实例调用反而多声称一份无用实例,同时误导读者


2. synchronized修饰对象
2.1加锁普通字段,自有实例发生争用

代码:

public class LockTest8 {
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest8().printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest8().printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	
	private String lockStr=new String("StrLock");
	public void printString(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printString "+i);
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	

	public void printDate(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printDate "+new Date().toLocaleString());
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

public class LockTest9 {
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest9().printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest9().printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	
	private static String lockStr=new String("StrLock");
	public void printString(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printString "+i);
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	

	public void printDate(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printDate "+new Date().toLocaleString());
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}


public class LockTest10 {
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest10().printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest10().printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	
	private Date lockDate=new Date();
	public void printString(){
		synchronized(lockDate){
			for (int i = 0; i < 3; i++) {
				System.out.println("printString "+i);
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	

	public void printDate(){
		synchronized(lockDate){
			for (int i = 0; i < 3; i++) {
				System.out.println("printDate "+new Date().toLocaleString());
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}


public class LockTest11 {
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest11().printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest11().printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	
	private static Date lockDate=new Date();
	public void printString(){
		synchronized(lockDate){
			for (int i = 0; i < 3; i++) {
				System.out.println("printString "+i);
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	

	public void printDate(){
		synchronized(lockDate){
			for (int i = 0; i < 3; i++) {
				System.out.println("printDate "+new Date().toLocaleString());
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

结论:

如“LockTest8.java”,一般普通字段都属于实例所有,每个实例都会创建一份属于自已的字段,对其加锁时,如果是自有实例则加锁对象不一样,不会争用;如果属于共享实例,则会发生争用。注意,这两个文件中都是用new String().不是直接使用字符串。一旦这类字段被static修饰,它就归类所有,不属于任何实例,无论创建多少实例,该字段都只创建一份,如(LockTest9.java),对这样的字段加锁,都会发生争用。LockTest10.java, LockTest11.java同样说明这一问题

2.2 加锁字符串

代码:

public class LockTest6 {
	public static void main(String[] args) {
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest6().printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					new LockTest6().printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	
	private String lockStr="StrLock";
	public void printString(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printString "+i);
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	

	public void printDate(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printDate "+new Date().toLocaleString());
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}


public class LockTest7 {
	public static void main(String[] args) {
		final LockTest7 instance = new LockTest7();
		new Thread("String thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					instance.printString();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
		new Thread("Date thread"){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					instance.printDate();
					try {
						Thread.currentThread().sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
	
	private String lockStr="StrLock";
	public void printString(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printString "+i);
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	

	public void printDate(){
		synchronized(lockStr){
			for (int i = 0; i < 3; i++) {
				System.out.println("printDate "+new Date().toLocaleString());
				try {
					Thread.currentThread().sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

结论:

         其创建方式也可以多样,最普遍的是直接使用。如我们使用“abc”不会去创建String str=new String("abc"),而是直接使用Stringstr="abc"。

         上面提到了newString的同步,new出来的对象都在堆中创建,堆中对象不共享。

         字符串直接使用会在常量池中创建,常量池的对象都是共享的,不管怎么使用,都只创建一次。这类对象的加锁,无论其本身是否被static修饰,都会产生争用(LockTest6.java,LockTest7.java)

         newString("abc")其实创建了两个对象"abc"本身在常量池创建,然后作为参数给构造器又在堆中创建一次。














猜你喜欢

转载自blog.csdn.net/calvin555555/article/details/50887414
今日推荐