Synchronized几种不同的同步方法、脏读、死锁代码样例讲解

1. 方法内的变量不存在非线程安全问题,永远都是线程安全的,这是因为方法内部的变量具有私有特性,不需要使用同步。

2. 当两个线程同时访问同一个业务对象中的没有同步的方法,并且同时操作业务对象中的实例变量,则有可能出现非线程安全问题。

例子如下

public class SynchronizedTest2 {

	public static void main(String[] args) {
		HasSelfPrivateNum numRef = new HasSelfPrivateNum();
		ThreadA aThread = new ThreadA(numRef);
		aThread.start();
		ThreadB bThread = new ThreadB(numRef);
		bThread.start();
	}
	
	static class HasSelfPrivateNum{
		private int num = 0;
		public void addI(String name) {
			try {
				if("a".equals(name)) {
					num = 100;
					System.out.println("a set over!");
					Thread.sleep(2000);
				}else {
					num = 200;	
					System.out.println("b set over!");
				}
				System.out.println(name + "num=" + num);
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private HasSelfPrivateNum numRef;
		public ThreadA(HasSelfPrivateNum numRef) {
			super();
			this.numRef = numRef;
		}
		
		@Override
		public void run() {
			super.run();
			numRef.addI("a");
		}
	}

	static class ThreadB extends Thread{
		private HasSelfPrivateNum numRef;
		public ThreadB(HasSelfPrivateNum numRef) {
			super();
			this.numRef = numRef;
		}
		
		@Override
		public void run() {
			super.run();
			numRef.addI("b");
		}
	}
}

运行结果为

a set over!
b set over!
bnum=200
anum=200

3. 如果业务对象和锁对象是属于一对一的关系,每个线程执行自己所属业务对象中的同步方法,不存在争抢关系,也是不需要锁的。

例子如下

public class SynchronizedTest3 {

	public static void main(String[] args) {
		HasSelfPrivateNum numRefA = new HasSelfPrivateNum();
		HasSelfPrivateNum numRefB = new HasSelfPrivateNum();
		ThreadA aThread = new ThreadA(numRefA);
		aThread.start();
		ThreadB bThread = new ThreadB(numRefB);
		bThread.start();
	}
	
	static class HasSelfPrivateNum{
		private int num = 0;
		synchronized public void addI(String name) {
			try {
				if("a".equals(name)) {
					num = 100;
					System.out.println("a set over!");
					Thread.sleep(2000);
				}else {
					num = 200;	
					System.out.println("b set over!");
				}
				System.out.println(name + "num=" + num);
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private HasSelfPrivateNum numRef;
		public ThreadA(HasSelfPrivateNum numRef) {
			super();
			this.numRef = numRef;
		}
		
		@Override
		public void run() {
			super.run();
			numRef.addI("a");
		}
	}

	static class ThreadB extends Thread{
		private HasSelfPrivateNum numRef;
		public ThreadB(HasSelfPrivateNum numRef) {
			super();
			this.numRef = numRef;
		}
		
		@Override
		public void run() {
			super.run();
			numRef.addI("b");
		}
	}
}

运行结果为

a set over!
b set over!
bnum=200
anum=100

4. 两个线程访问同一个对象的两个方法

此时有以下结论

  1. A线程现持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的非synchronized类型的方法。
  2. A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法,则需要等待,也就是同步。
  3. 在方法声明处添加synchronized并不是锁方法,而是锁当前类的对象。
  4. 在Java中只有“当对象作为锁”这种说法,并没有“锁方法”这种说法。
  5. 在Java语言中,“锁”就是“对象”,“对象”可以映射成“锁”,哪个线程拿到这把锁,哪个线程就可以执行这个对象中的synchronized同步方法。
  6. 如果在对象中使用了synchronized关键字声明非静态方法,则对象就被当成锁。

例子1:两个线程分别调用同一个对象的同步方法和非同步方法

public class SynchronizedTest4_1 {

	public static void main(String[] args) {
		MyObject obejct = new MyObject();
		ThreadA aThread = new ThreadA(obejct);
		aThread.start();
		ThreadB bThread = new ThreadB(obejct);
		bThread.start();
	}
	
	static class MyObject{
		synchronized public void methodA() {
			try {
				System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
				Thread.sleep(5000);
				System.out.println("end endTime=" + System.currentTimeMillis());
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		public void methodB() {
			try {
				System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
				Thread.sleep(5000);
				System.out.println("end endTime=" + System.currentTimeMillis());
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private MyObject object;
		public ThreadA(MyObject object) {
			super();
			this.object = object;
		}
		
		@Override
		public void run() {
			super.run();
			object.methodA();
		}
	}

	static class ThreadB extends Thread{
		private MyObject object;
		public ThreadB(MyObject object) {
			super();
			this.object = object;
		}
		
		@Override
		public void run() {
			super.run();
			object.methodB();
		}
	}
}

运行结果为

begin methodA threadName=Thread-0
begin methodB threadName=Thread-1
end endTime=1603249231399
end endTime=1603249231399

例子2:两个线程分别调用同一个对象的两个不同的同步方法

public class SynchronizedTest4_2 {

	public static void main(String[] args) {
		MyObject obejct = new MyObject();
		ThreadA aThread = new ThreadA(obejct);
		aThread.start();
		ThreadB bThread = new ThreadB(obejct);
		bThread.start();
	}
	
	static class MyObject{
		synchronized public void methodA() {
			try {
				System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
				Thread.sleep(5000);
				System.out.println("end endTime=" + System.currentTimeMillis());
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		synchronized public void methodB() {
			try {
				System.out.println("begin methodB threadName=" + Thread.currentThread().getName());
				Thread.sleep(5000);
				System.out.println("end endTime=" + System.currentTimeMillis());
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private MyObject object;
		public ThreadA(MyObject object) {
			super();
			this.object = object;
		}
		
		@Override
		public void run() {
			super.run();
			object.methodA();
		}
	}

	static class ThreadB extends Thread{
		private MyObject object;
		public ThreadB(MyObject object) {
			super();
			this.object = object;
		}
		
		@Override
		public void run() {
			super.run();
			object.methodB();
		}
	}
}

运行结果为:

begin methodA threadName=Thread-0
end endTime=1603249500001
begin methodB threadName=Thread-1
end endTime=1603249505002

5. 脏读

我们有时候在赋值的方法进行了同步,但是在取值的方法没有进行同步,就有可能出现脏读(dirty read)
例子:

public class SynchronizedTest5 {

	public static void main(String[] args) {
		PublicVar publicVar = new PublicVar();
		ThreadA aThread = new ThreadA(publicVar);
		aThread.start();
		ThreadB bThread = new ThreadB(publicVar);
		bThread.start();
	}
	
	static class PublicVar{
		public String username = "A";
		public String password = "AA";
		synchronized public void setValue(String username, String password) {
			try {
				this.username = username;
				Thread.sleep(5000);
				this.password = password;
				System.out.println("setValue method thread name=" + Thread.currentThread().getName()
						+ " username" + username + " password=" + password);
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		public void getValue() {
			System.out.println("getValue method thread name=" + Thread.currentThread().getName()
					+ " username" + username + " password=" + password);
		}
	}

	static class ThreadA extends Thread{
		private PublicVar publicVar;
		public ThreadA(PublicVar publicVar) {
			super();
			this.publicVar = publicVar;
		}
		
		@Override
		public void run() {
			super.run();
			publicVar.setValue("B", "BB");
		}
	}

	static class ThreadB extends Thread{
		private PublicVar publicVar;
		public ThreadB(PublicVar publicVar) {
			super();
			this.publicVar = publicVar;
		}
		
		@Override
		public void run() {
			super.run();
			publicVar.getValue();
		}
	}
}

运行结果为:

getValue method thread name=Thread-1 usernameB password=AA
setValue method thread name=Thread-0 usernameB password=BB

6. Synchronized锁重入

“可重入锁”是指自己可以再次获取自己的内部锁。例如。一个线程获得了某个对象锁,此时这个对象锁还没有释放,当其再次想要获取这个对象锁时还是可以获取的。
并且锁重入支持继承的环境
例子

public class SynchronizedTest6 {

	public static void main(String[] args) {
		MyThreadA t1 = new MyThreadA();
		t1.start();
		
		MyThreadB t2 = new MyThreadB();
		t2.start();
	}
	
	static class Main{
		public int i = 10;
		synchronized public void operateIMainMethod() {
			try {
				i--;
				System.out.println("main pint i=" + i);
				Thread.sleep(100);

			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	static class Sub extends Main{
		public int i = 10;
		synchronized public void operateISubMethod() {
			try {
				i--;
				System.out.println("sub pint i=" + i);
				Thread.sleep(1000);
				super.operateIMainMethod();

			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	static class Sub2 extends Main{
		public int i = 10;
		synchronized public void operateISubMethod() {
			try {
				i--;
				System.out.println("sub2 pint i=" + i);
				Thread.sleep(1000);
				super.operateIMainMethod();

			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class MyThreadA extends Thread{
		@Override
		public void run() {
			Sub sub = new Sub();
			sub.operateISubMethod();
		}
	}
	
	static class MyThreadB extends Thread{
		@Override
		public void run() {
			Sub2 sub2 = new Sub2();
			sub2.operateISubMethod();
		}
	}
}

运行结果

sub pint i=9
sub2 pint i=9
main pint i=9
main pint i=9

7. 出现异常,锁会自动释放

当一个线程执行的代码出现异常时,其所持有的锁会自动释放。Thread.java类中suspend()方法和sleep(millis)方法被调用后并不释放锁。

8. 重写方法

重写方法如果不使用synchronized关键字,即是非同步方法,使用后变成同步方法。

9. 单一方法内的sychronized同步语句块

用关键字synchronized声明方法在某些情况下是有弊端的,例如:A线程调用同步方法执行一个长时间的任务,那么B线程等待的时间就比较长,这种情况可以使用synchronized同步语句块来解决,以提高运行效率。此时只有运行到同步语句块的时候才会需要获取锁,方法内的其他代码都不需要锁。
例子1:synchronized声明方法

public class Task{
	public String getData1;
	public String getData2;
	synchronized public void doLongTimeTask() {
		try {
			System.out.println("begin task");
			Thread.sleep(3000);
			getData1 = "长时间处理任务后从远程返回的值1 threadName=" 
					+ Thread.currentThread().getName();
			getData2 = "长时间处理任务后从远程返回的值2 threadName=" 
					+ Thread.currentThread().getName();
			System.out.println(getData1);
			System.out.println(getData2);
			System.out.println("end task");
		}catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

例子2:synchronized同步语句块

static class Task{
	public String getData1;
	public String getData2;
	public void doLongTimeTask() {
		try {
			System.out.println("begin task");
			Thread.sleep(3000);
			synchronized(this) {
				getData1 = "长时间处理任务后从远程返回的值1 threadName=" 
						+ Thread.currentThread().getName();
				getData2 = "长时间处理任务后从远程返回的值2 threadName=" 
						+ Thread.currentThread().getName();
				System.out.println(getData1);
				System.out.println(getData2);
				System.out.println("end task");
			}
		}catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

10. 多个synchronized同步语句块

在使用同步synchronized(this)代码块时需要注意,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞。
我们常用的PrintStream.println方法也是使用的同步代码块。

11. synchronized同步语句块和synchronized修饰的方法

使用synchronized(this)同步代码块将当前类的对象作为锁,使用synchronized修饰的方法也是将当前类所在的对象作为锁,都是同一把锁,所以会相互阻塞。
例子

public class SynchronizedTest11 {

	public static void main(String[] args) {
		Task task = new Task();
		ThreadA aThread = new ThreadA(task);
		aThread.start();
		ThreadB bThread = new ThreadB(task);
		bThread.start();
	}
	
	static class Task{
		public void doLongTimeTask() {
			try {
				synchronized(this) {
					System.out.println("=======run===doLongTimeTask====start");
					Thread.sleep(5000);
					System.out.println("=======run===doLongTimeTask====end");
				}
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		synchronized public void otherMethod() {
			System.out.println("-----run---otherMethod");
		}
	}

	static class ThreadA extends Thread{
		private Task task;
		public ThreadA(Task task) {
			super();
			this.task = task;
		}
		
		@Override
		public void run() {
			super.run();
			task.doLongTimeTask();
		}
	}

	static class ThreadB extends Thread{
		private Task task;
		public ThreadB(Task task) {
			super();
			this.task = task;
		}
		
		@Override
		public void run() {
			super.run();
			task.otherMethod();
		}
	}
}

运行结果:

=======run===doLongTimeTask====start
=======run===doLongTimeTask====end
-----run---otherMethod

12. 将任意对象作为锁

多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronzied(this)同步代码块时,调用的效果都是按顺序执行,即同步。
synchronized同步代码块和synchronized同步方法基本一样,都是下面两个规则:

  1. 对于其他synchronized同步方法或synchronized(this)同步代码块调用呈同步效果
  2. 同一时间只有一个线程可以执行synchronized同步方法(synchronized同步代码块)中的代码

除了使用synchronized(this)格式来创建同步代码块,其实Java还支持将“任意对象”作为锁来实现同步的功能,这个“任意对象”大多数是实例变量及方法的参数。使用格式为synchronized(非this对象)。
synchronized(非this对象X)同步代码的作用:当多个线程争抢相同的“非this对象X”的锁时,同一时间只有一个线程可以执行synchronized(非this对象X)同步代码块中的代码。
例子:

public class SynchronizedTest12 {

	public static void main(String[] args) {
		Service service = new Service();
		ThreadA aThread = new ThreadA(service);
		aThread.start();
		ThreadB bThread = new ThreadB(service);
		bThread.start();
	}
	
	static class Service{
		private String anyString = new String();
		public void a() {
			try {
				synchronized(anyString) {
					System.out.println("in synchronized anyString, threadName="
						 + Thread.currentThread().getName());
					Thread.sleep(5000);
					System.out.println("exit synchronized anyString, threadName="
					 	 + Thread.currentThread().getName());
				}
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private Service service;
		public ThreadA(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.a();
		}
	}

	static class ThreadB extends Thread{
		private Service service;
		public ThreadB(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.a();
		}
	}
}

运行结果:

in synchronized anyString, threadName=Thread-0
exit synchronized anyString, threadName=Thread-0
in synchronized anyString, threadName=Thread-1
exit synchronized anyString, threadName=Thread-1

13. 同一个对象中的不同锁

如果synchronized(对象X)中的对象不是同一个对象,那么即便在同一个对象锁,也不会产生锁竞争。
例子:

public class SynchronizedTest13 {

	public static void main(String[] args) {
		Service service = new Service();
		ThreadA aThread = new ThreadA(service);
		aThread.start();
		ThreadB bThread = new ThreadB(service);
		bThread.start();
	}
	
	static class Service{
		private String anyString = new String();
		public void a() {
			try {
				synchronized(anyString) {
					System.out.println("in a, threadName=" 
							+ Thread.currentThread().getName());
					Thread.sleep(5000);
					System.out.println("exit a, threadName=" 
							+ Thread.currentThread().getName());
				}
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		synchronized public void b() {
			System.out.println("in b, threadName=" 
					+ Thread.currentThread().getName());
		}
	}

	static class ThreadA extends Thread{
		private Service service;
		public ThreadA(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.a();
		}
	}

	static class ThreadB extends Thread{
		private Service service;
		public ThreadB(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.b();
		}
	}
}

运行结果:

in a, threadName=Thread-0
in b, threadName=Thread-1
exit a, threadName=Thread-0

14. 方法被调用时随机的

同步代码块放在非同步synchronized方法中进行声明,并不能保证调用方法的线程的执行同步顺序性,也就是线程调用方法的顺序是无序的,虽然在同步块中执行的顺序是同步的。
例子:

public class SynchronizedTest14 {

	public static void main(String[] args) {
		MyList list = new MyList();
		ThreadA aThread = new ThreadA(list);
		aThread.setName("A");
		aThread.start();
		ThreadB bThread = new ThreadB(list);
		bThread.setName("B");
		bThread.start();
	}
	
	static class MyList{
		synchronized public void add(String username) {
			System.out.println("in add,threadName=" 
					+ Thread.currentThread().getName());
			System.out.println("exit add,threadName=" 
					+ Thread.currentThread().getName());
		}
	}

	static class ThreadA extends Thread{
		private MyList list;
		public ThreadA(MyList list) {
			super();
			this.list = list;
		}
		
		@Override
		public void run() {
			super.run();
			for(int i = 1; i < 1000; i++) {
				list.add("threadA" + i);
			}
		}
	}

	static class ThreadB extends Thread{
		private MyList list;
		public ThreadB(MyList list) {
			super();
			this.list = list;
		}
		
		@Override
		public void run() {
			super.run();
			for(int i = 1; i < 1000; i++) {
				list.add("threadB" + i);
			}
		}
	}
}

运行结果太过于长,只截取部分

in add,threadName=A
exit add,threadName=A
in add,threadName=A
exit add,threadName=A
in add,threadName=A
exit add,threadName=A
in add,threadName=B
exit add,threadName=B
in add,threadName=B
exit add,threadName=B
in add,threadName=A
exit add,threadName=A
in add,threadName=A
exit add,threadName=A
in add,threadName=B
exit add,threadName=B
in add,threadName=A
exit add,threadName=A

15. 不同步导致的逻辑错误

有些方法不进行同步化,则会出现逻辑上的错误
例子如下:

public class SynchronizedTest15 {

	public static void main(String[] args) throws InterruptedException {
		MyOneList list = new MyOneList();
		ThreadA aThread = new ThreadA(list);
		aThread.setName("A");
		aThread.start();
		ThreadB bThread = new ThreadB(list);
		bThread.setName("B");
		bThread.start();
		
		Thread.sleep(6000);
		System.out.println("listSize=" + list.getSize());
	}
	
	static class MyOneList{
		private List<String> list = new ArrayList<String>();
		synchronized public void add(String data) {
			list.add(data);
		}
		synchronized public int getSize() {
			return list.size();
		}
	}
	
	static class MyService{
		public MyOneList addServiceMethod(MyOneList list, String data) {
			try {
				if(list.getSize() < 1) {
					Thread.sleep(2000);
					list.add(data);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return list;
		}
	}

	static class ThreadA extends Thread{
		private MyOneList list;
		public ThreadA(MyOneList list) {
			super();
			this.list = list;
		}
		
		@Override
		public void run() {
			super.run();
			MyService msRef = new MyService();
			msRef.addServiceMethod(list, "A");
		}
	}

	static class ThreadB extends Thread{
		private MyOneList list;
		public ThreadB(MyOneList list) {
			super();
			this.list = list;
		}
		
		@Override
		public void run() {
			super.run();
			MyService msRef = new MyService();
			msRef.addServiceMethod(list, "B");
		}
	}
}

运行结果为

listSize=2

这就是没有对MyService.addServiceMethod()方法进行同步导致的错误

16. 静态同步方法synchronized方法和synchronized(class)代码块

关键字synchronized还可以应用在static静态方法上,如果这样写,那是对当前的*.java文件对应的Class类对象进行持锁,Class类的对象是单例的,更具体的说,在静态static方法上使用synchronized关键字声明同步方法时,使用当前静态方法所在类对应Class类的单例对象作为锁。
例子1:静态同步方法和非静态同步方法是不同的锁

public class SynchronizedTest16 {

	public static void main(String[] args) throws InterruptedException {
		Service service = new Service();
		ThreadA aThread = new ThreadA(service);
		aThread.setName("A");
		aThread.start();
		ThreadB bThread = new ThreadB(service);
		bThread.setName("B");
		bThread.start();
	}
	
	static class Service{
		synchronized public static void printA() {
			try {
				System.out.println("in A ,threadName=" + Thread.currentThread().getName());
				Thread.sleep(3000);
				System.out.println("exit A ,threadName=" + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		synchronized public void printB() {
			try {
				System.out.println("in B ,threadName=" + Thread.currentThread().getName());
				Thread.sleep(3000);
				System.out.println("exit B ,threadName=" + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private Service service;
		public ThreadA(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.printA();
		}
	}

	static class ThreadB extends Thread{
		private Service service;
		public ThreadB(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.printB();
		}
	}
}

运行结果

in A ,threadName=A
in A ,threadName=B
exit A ,threadName=B
exit A ,threadName=A

例子2:同一个类的不同对象的不同synchronized静态方法也是同一把锁

public class SynchronizedTest16_2 {

	public static void main(String[] args) throws InterruptedException {
		Service service1 = new Service();
		ThreadA aThread = new ThreadA(service1);
		aThread.setName("A");
		aThread.start();
		Service service2 = new Service();
		ThreadB bThread = new ThreadB(service2);
		bThread.setName("B");
		bThread.start();
	}
	
	static class Service{
		synchronized public static void printA() {
			try {
				System.out.println("in A ,threadName=" + Thread.currentThread().getName());
				Thread.sleep(3000);
				System.out.println("exit A ,threadName=" + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		synchronized public static void printB() {
			try {
				System.out.println("in B ,threadName=" + Thread.currentThread().getName());
				Thread.sleep(3000);
				System.out.println("exit B ,threadName=" + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private Service service;
		public ThreadA(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.printA();
		}
	}

	static class ThreadB extends Thread{
		private Service service;
		public ThreadB(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.printB();
		}
	}
}

运行结果

in A ,threadName=A
exit A ,threadName=A
in B ,threadName=B
exit B ,threadName=B

例子3:synchronized静态方法和synchronized(Class)是同一把锁

public class SynchronizedTest16_3 {

	public static void main(String[] args) throws InterruptedException {
		Service service1 = new Service();
		ThreadA aThread = new ThreadA(service1);
		aThread.setName("A");
		aThread.start();
		Service service2 = new Service();
		ThreadB bThread = new ThreadB(service2);
		bThread.setName("B");
		bThread.start();
	}
	
	static class Service{
		synchronized public static void printA() {
			try {
				System.out.println("in A ,threadName=" + Thread.currentThread().getName());
				Thread.sleep(3000);
				System.out.println("exit A ,threadName=" + Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		public void printB() {
			try {
				synchronized(Service.class) {
					System.out.println("in B ,threadName=" 
						+ Thread.currentThread().getName());
					Thread.sleep(3000);
					System.out.println("exit B ,threadName=" 
						+ Thread.currentThread().getName());
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private Service service;
		public ThreadA(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.printA();
		}
	}

	static class ThreadB extends Thread{
		private Service service;
		public ThreadB(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.printB();
		}
	}
}

运行结果

in A ,threadName=A
exit A ,threadName=A
in B ,threadName=B
exit B ,threadName=B

17. String常量池特性和同步相关问题

JVM具有String常量池的功能,当synchronized(string)同步块与String联合使用时,要注意常量池带来的一些意外。
当你传入的String的值是一样的时候,锁可能就是相同的。
例子

public class SynchronizedTest17 {

	public static void main(String[] args) throws InterruptedException {
		Service service = new Service();
		ThreadA aThread = new ThreadA(service);
		aThread.setName("A");
		aThread.start();
		ThreadB bThread = new ThreadB(service);
		bThread.setName("B");
		bThread.start();
	}
	
	static class Service{
		public static void print(String stringParam) {
			try {
				synchronized(stringParam) {
					while(true) {
						System.out.println("in print,threadName=" 
							+ Thread.currentThread().getName());
						Thread.sleep(1000);
						System.out.println("exit print,threadName=" 
							+ Thread.currentThread().getName());
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	static class ThreadA extends Thread{
		private Service service;
		public ThreadA(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.print("AA");
		}
	}

	static class ThreadB extends Thread{
		private Service service;
		public ThreadB(Service service) {
			super();
			this.service = service;
		}
		
		@Override
		public void run() {
			super.run();
			service.print("AA");
		}
	}
}

截取部分运行结果:

in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A
in print,threadName=A
exit print,threadName=A

18. 多线程的死锁

Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成。在多线程技术中,“死锁”是必须避免的,因为这会造成线程“假死”。
例子:

public class SynchronizedTest18 {

	public static void main(String[] args) throws InterruptedException {
		DealThread t1 = new DealThread();
		t1.setFlag("a");
		Thread thread1 = new Thread(t1);
		thread1.start();
		
		Thread.sleep(1000);
		
		t1.setFlag("b");
		Thread thread2 = new Thread(t1);
		thread2.start();
	}
	
	static class DealThread implements Runnable{
		public String username;
		public Object lock1 = new Object();
		public Object lock2 = new Object();
		public void setFlag(String username) {
			this.username = username;
		}
		
		@Override
		public void run() {
			if("a".equals(username)) {
				synchronized (lock1) {
					try {
						System.out.println("username= " + username);
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					synchronized(lock2) {
						System.out.println("按lock1->lock2代码顺序执行了");
					}
				}
			}
			if("b".equals(username)) {
				synchronized (lock2) {
					try {
						System.out.println("username= " + username);
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					synchronized(lock1) {
						System.out.println("按lock2->lock1代码顺序执行了");
					}
				}
			}
		}
	}
}

运行结果

username= a
username= b

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值