Java多线程学习笔记9之对象及变量的并发访问

版权声明:分享才能获得最大的价值 https://blog.csdn.net/qq_32252957/article/details/83187460

本文是我学习Java多线程以及高并发知识的第一本书的学习笔记,
书名是<<Java多线程编程核心技术>>,作者是大佬企业高级项目经理
高洪岩前辈,在此向他致敬。我将配合开发文档以及本书和其他的博客
奉献着的文章来学习,同时做一些简单的总结。有些基础的东西我就不
细分析了,建议以前接触过其他语言多线程或者没有系统学习过多线程
的开发者来看。另外需要注意的是,博客中给出的一些英文文档我就简单
翻译了,重要的部分会详细翻译,要不太浪费时间了,这个是我想提高
自己的英文阅读水平和文档查看能力,想要积攒内功的人可以用有谷歌
翻译自己看文档细读。(中文文档建议只参考,毕竟你懂得...)
详细代码见:
github代码地址

本节内容:

1) 多线程死锁条件

2) 成员内部类和静态内部类同步

3) 锁对象改变和锁对象属性的改变

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

1) 互斥使用
    即当资源被一个线程使用(占有)时,别的线程不能使用
2) 不可抢占
    资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放
3) 请求和保持
    即当资源的请求者在请求其他的资源的同时保持对原有资源的持有
4) 循环等待
    即存在一个等待队列: p1占有p2的资源,p2占有p3的资源,p3占有p1的资源。这样
就形成了一个等待环路。
当上述四个条件都成立,便形成死锁。

举个例子:

package chapter02.section02.thread_2_2_12.project_1_deadLockTest;

public 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(username.equals("a")) {
			synchronized(lock1) {
				try {
					System.out.println("username = " + username);
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					// TODO: handle exception
					e.printStackTrace();
				}
				synchronized(lock2) {
					System.out.println("按lock1->lock2代码顺序执行了");
				}
			}
		}
		if(username.equals("b")) {
			synchronized(lock2) {
				try {
					System.out.println("username = " + username);
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					// TODO: handle exception
					e.printStackTrace();
				}
				synchronized(lock1) {
					System.out.println("按lock2->lock2代码顺序执行了");
				}
			}
		}
	}
}

package chapter02.section02.thread_2_2_12.project_1_deadLockTest;

public class Run {
	public static void main(String args[]) {
		try {
			DealThread t1 = new DealThread();
			t1.setFlag("a");
			
			Thread thread1 = new Thread(t1);
			thread1.start();
			
			Thread.sleep(100);
			
			t1.setFlag("b");
			Thread thread2 = new Thread(t1);
			thread2.start();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}

/*
result:
username = a
username = b
*/

结果分析:
可以看到死锁发生了
可以使用JDk自带的工具来检测是否有死锁的现象


可以看到运行的线程的tid值是8376

可以使用jstack命令来查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁)
运行Jstack -l 8376查看死锁的具体情况

可以看到Thread-0和Thread-1拥有的锁以及正在等待要持有的锁,发现Thread-1要申
请的锁与Thread-0持有的锁是一样的,这是造成死锁的原因,是根据死锁的第二个条件
来判断的。
本实验使用synchronized嵌套的代码结构来实现死锁,其实不使用synchronized代码结
构也会出现死锁,与嵌套不嵌套没有任何的关系,只要互相等待对方释放锁就有可能出现
死锁

2. 内置类(成员内部类)与静态内置类
成员内部类:

     内部类中最常见的就是成员内部类,也称为普通内部类。作为外部类的一个成员存在,
与外部类的属性、方法并列。

举例:

package chapter02.section02.thread_2_2_13.project_1_innerClass;

public class PublicClass {
	
	private String username;
	private String password;
	
	class PrivateClass{
		private String age;
		private String address;
		public String getAge() {
			return age;
		}
		public void setAge(String age) {
			this.age = age;
		}
		public String getAddress() {
			return address;
		}
		public void setAddress(String address) {
			this.address = address;
		}
		
		public void printPublicProperty() {
			System.out.println(username + " " + password);
		}
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}


package chapter02.section02.thread_2_2_13.project_1_innerClass;

//import chapter02.section02.thread_2_2_13.project_1_innerClass.PublicClass.PrivateClass;

public class Run {
	public static void main(String[] args) {
		
		PublicClass publicClass = new PublicClass();
		publicClass.setUsername("usernameValue");
		publicClass.setPassword("passwordValue");
		
		System.out.println(publicClass.getUsername() + " " 
				+ publicClass.getPassword());
		
		/**
		 * 成员内部类是依赖外部类而存在的,即,如果要创建成员
		 * 内部类的对象,前提是必须存在一个外部类对象。
		 * 这里对成员内部类不做详细介绍
		 */
		PublicClass.PrivateClass privateClass = publicClass.new PrivateClass();
		privateClass.setAge("ageValue");
		privateClass.setAddress("addressValue");
		
		System.out.println(privateClass.getAge() + " " 
				+ privateClass.getAddress());
	}
}
/*
result:
usernameValue passwordValue
ageValue addressValue
*/

静态内部类:

    静态内部类可以使用public,protected,private修饰.静态内部类中可以定义静
态和非静态的成员

举个例子:

package chapter02.section02.thread_2_2_13.project_2_innerStaticClass;

public class PublicClass {
	
	static private String username;
	static private String password;
	
	static class PrivateClass{
		private String age;
		private String address;
		public String getAge() {
			return age;
		}
		public void setAge(String age) {
			this.age = age;
		}
		public String getAddress() {
			return address;
		}
		public void setAddress(String address) {
			this.address = address;
		}
		
		public void printPublicProperty() {
			System.out.println(username + " " + password);
		}
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

package chapter02.section02.thread_2_2_13.project_2_innerStaticClass;


public class Run {
	public static void main(String[] args) {
		
		PublicClass publicClass = new PublicClass();
		publicClass.setUsername("usernameValue");
		publicClass.setPassword("passwordValue");
		
		System.out.println(publicClass.getUsername() + " " 
				+ publicClass.getPassword());
		
		/**
		 * 静态内部类,它是不需要依赖于外部类的,并且它
		 * 不能使用外部类的非static成员变量或方法
		 */
		PublicClass.PrivateClass privateClass = new PublicClass.PrivateClass();
		privateClass.setAge("ageValue");
		privateClass.setAddress("addressValue");
		privateClass.setAddress("addressValue");
	}
}
/*
result:
usernameValue passwordValue
ageValue addressValue
*/

(1) 内置类与同步实验1
实验中在内置类中有两个同步方法,但使用的却是不同的锁,打印的结果是异步的。

package chapter02.section02.thread_2_2_14.project_1_innerTest1;

public class OutClass {
	
	static class Inner{
		public void method1() {
			synchronized("其他的锁") {
				for(int i = 1; i <= 10; i++) {
					System.out.println(Thread.currentThread().getName() + " i="
							+ i);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO: handle exception
						e.printStackTrace();
					}
				}
			}
		}
		
		public synchronized void method2() {
			for(int i = 1; i <= 20; i++) {
				System.out.println(Thread.currentThread().getName() + " i="
						+ i);
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO: handle exception
					e.printStackTrace();
				}
			}
		}
	}
}


package chapter02.section02.thread_2_2_14.project_1_innerTest1;
import chapter02.section02.thread_2_2_14.project_1_innerTest1.OutClass.Inner;

public class Run {
	public static void main(String[] args) {
		final Inner inner = new Inner();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
			inner.method1();
			}	
		}, "A");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				inner.method2();
			}
		}, "B");
		
		t1.start();
		t2.start();
	}
}
/*
可以看到结果是异步执行的,因为线程持有不同的"对象监视器"
result:
......
B i=8
A i=8
A i=9
B i=9
A i=10
......
*/

(2) 内置类与同步实验2
测试同步代码块synchronized(class2)对class2上锁后,其他线程只能以同步的方式
调用class2中的synchronized同步方法

package chapter02.section02.thread_2_2_15.project_1_innerTest2;

public class OutClass {
	static class InnerClass1{
		public void method1(InnerClass2 class2) {
			String threadName = Thread.currentThread().getName();
			synchronized(class2) {
				System.out.println(threadName + 
						" 进入InnerClass1类中的method1方法");
				for (int i = 0; i < 10; i++) {
					System.out.println("i=" + i);
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {

					}
				}
				System.out.println(threadName + 
						" 离开InnerClass1类中的method1方法");
			}
		}
		
		public synchronized void method2() {
			String threadName = Thread.currentThread().getName();
			System.out.println(threadName + " 进入InnerClass1类中的method2方法");
			for (int j = 0; j < 10; j++) {
				System.out.println("j=" + j);
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

				}
			}
			System.out.println(threadName + " 离开InnerClass1类中的method2方法");
		}
	}
	
	static class InnerClass2{
		public synchronized void method1() {
			String threadName = Thread.currentThread().getName();
			System.out.println(threadName + 
					" 进入InnerClass2类中的method1方法");
			for (int k = 0; k < 10; k++) {
				System.out.println("k=" + k);
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

				}
			}
			System.out.println(threadName + " 离开InnerClass2类中的method1方法");
		}
	}
}


package chapter02.section02.thread_2_2_15.project_1_innerTest2;

import chapter02.section02.thread_2_2_15.project_1_innerTest2.OutClass.InnerClass1;
import chapter02.section02.thread_2_2_15.project_1_innerTest2.OutClass.InnerClass2;

public class Run {
	public static void main(String[] args) {
		final InnerClass1 in1 = new InnerClass1();
		final InnerClass2 in2 = new InnerClass2();
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				in1.method1(in2);
			}
		}, "T1");
		Thread t2 = new Thread(new Runnable() {
			public void run() {
				in1.method2();
			}
		}, "T2");
		Thread t3 = new Thread(new Runnable() {
			public void run() {
				in2.method1();
			}
		}, "T3");
		t1.start();
		t2.start();
		t3.start();
	}
}
/*
result:
T1 进入InnerClass1类中的method1方法
i=0
T2 进入InnerClass1类中的method2方法
j=0
j=1
i=1
i=2
j=2
i=3
j=3
j=4
i=4
j=5
i=5
i=6
j=6
j=7
i=7
j=8
i=8
i=9
j=9
T2 离开InnerClass1类中的method2方法
T1 离开InnerClass1类中的method1方法
T3 进入InnerClass2类中的method1方法
k=0
k=1
k=2
k=3
k=4
k=5
k=6
k=7
k=8
k=9
T3 离开InnerClass2类中的method1方法
*/

3. 锁对象的改变
在将任何类型数据作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象,如
果同时持有相同的锁对象,则这些线程之间就是同步的;如果分别获得锁对象,这些线程
之间就是异步的。

举例:

package chapter02.section02.thread_2_2_16.project_1_setNewStringTwoLock;

public class MyService {
	private String lock = "123";
	
	public void testMethod() {
		try {
			synchronized(lock) {
				System.out.println(Thread.currentThread().getName() + " begin "
						+ System.currentTimeMillis());
				lock = "456";
				Thread.sleep(2000);
				System.out.println(Thread.currentThread().getName() + " end "
						+ System.currentTimeMillis());
			}
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}


package chapter02.section02.thread_2_2_16.project_1_setNewStringTwoLock;

public class ThreadA extends Thread {
	private MyService service;

	public ThreadA(MyService service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		service.testMethod();
	}
}


package chapter02.section02.thread_2_2_16.project_1_setNewStringTwoLock;

public class ThreadB extends Thread{
	private MyService service;

	public ThreadB(MyService service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		service.testMethod();
	}
}


package chapter02.section02.thread_2_2_16.project_1_setNewStringTwoLock;

public class Run1 {
	public static void main(String[] args) throws InterruptedException{
		MyService service = new MyService();
		
		ThreadA a = new ThreadA(service);
		a.setName("A");
		
		ThreadB b = new ThreadB(service);
		b.setName("B");
		
		a.start();
		Thread.sleep(50);
		b.start();
	}
}
/*
A begin 1539932674955
B begin 1539932675007
A end 1539932676958
B end 1539932677008
*/


package chapter02.section02.thread_2_2_16.project_1_setNewStringTwoLock;

public class Run2 {
	public static void main(String[] args) throws InterruptedException{
		MyService service = new MyService();

		ThreadA a = new ThreadA(service);
		a.setName("A");

		ThreadB b = new ThreadB(service);
		b.setName("B");

		a.start();
		b.start();
	}
}
/*
result:
A begin 1539932865275
A end 1539932867283
B begin 1539932867283
B end 1539932869294
 */

结果分析:
Run1类中运行结果是异步执行,因为50ms之后线程B取得的锁是"456"
Run2类中运行结果是同步执行,因为线程A、B持有的锁都是"123",
虽然将锁改成了"456",但结果还是同步的,因为A、B共同争抢的是"123"

注: 只有对象不变,即使对象的属性被改变,运行的结果还是同步的
举例:

package chapter02.section02.thread_2_2_16.project_2_setNewPropertiesLockOne;

public class Userinfo {
	private String username;
	private String password;
	
	public Userinfo() {
		super();
	}
	
	public Userinfo(String username, String password) {
		super();
		this.username = username;
		this.password = password;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}


package chapter02.section02.thread_2_2_16.project_2_setNewPropertiesLockOne;

public class MyService {
	
	public void serviceMethodA(Userinfo userinfo) {
		synchronized (userinfo) {
			try {
				System.out.println(Thread.currentThread().getName());
				userinfo.setUsername("abcabcabc"); //对象属性改变
				Thread.sleep(3000);
				System.out.println("end! time=" + System.currentTimeMillis());
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


package chapter02.section02.thread_2_2_16.project_2_setNewPropertiesLockOne;

public class ThreadA extends Thread {
	private MyService service;
	private Userinfo userinfo;

	public ThreadA(MyService service, Userinfo userinfo) {
		super();
		this.service = service;
		this.userinfo = userinfo;
	}

	@Override
	public void run() {
		service.serviceMethodA(userinfo);
	}
}


package chapter02.section02.thread_2_2_16.project_2_setNewPropertiesLockOne;

public class ThreadB extends Thread{
	private MyService service;
	private Userinfo userinfo;

	public ThreadB(MyService service, Userinfo userinfo) {
		super();
		this.service = service;
		this.userinfo = userinfo;
	}

	@Override
	public void run() {
		service.serviceMethodA(userinfo);
	}
}


package chapter02.section02.thread_2_2_16.project_2_setNewPropertiesLockOne;

public class Run {
	public static void main(String[] args) {

		try {
			MyService service = new MyService();
			Userinfo userinfo = new Userinfo();

			ThreadA a = new ThreadA(service, userinfo);
			a.setName("a");
			a.start();
			Thread.sleep(50);
			ThreadB b = new ThreadB(service, userinfo);
			b.setName("b");
			b.start();

		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
/*
可以看到对象不变,属性变化时同步效果
时间相差3000毫秒
result:
a
end! time=1539933771256
b
end! time=1539933774256
*/

猜你喜欢

转载自blog.csdn.net/qq_32252957/article/details/83187460
今日推荐