第二章、对象及变量的并发访问 之 synchronized 同步方法(2)
2017-09-25 10:24
399 查看
5. 脏读:发生脏读的情况是在读取示例变量时,此值已经被其他线程更改过了。public class PublicVar {
public String username = "A";
public String password = "AA";
public synchronized void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method threadname = " + Thread.currentThread().getName() + " username = " + username + " password = " + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue() {
System.out.println("getValue method threadname = " + Thread.currentThread().getName() + " username = " + username + " password = " + password);
}
}
解释一下运行结果:线程A调用 publicVar对象的synchronized 的 setValue() 方法,执行完 username 的赋值为 B之后,执行 sleep() 方法休眠5s。这时 main线程执行非 synchronized getValue() 方法,此时 username 已经被线程A赋值为 B了。所以有执行结果:
由此可知,当线程A调用对象Object 的synchronized 方法 X 时,A线程获得了 X 的方法锁,更准确的说是 对象Object锁。所以其他线程想要再执行 X 方法必须等线程A执行完X方法。但是其他线程可以调用对象Object中的其他 非synchronized 同步方法。
为了避免脏读,我们将setValue 也加上synchronized关键字变成同步方法,那么结果就变成:main 将要执行 getValue() 方法
setValue method threadname = Thread-0 username = B password = BB
getValue method threadname = main username = B password = BB
getValue() 方法加上synchronized 之后,main线程调用必须等A线程执行完 setValue() 之后,也就是说 username和password 都已经被重新赋值了。然后main线程才能调用 getValue() 方法,不存在脏读环境。
6. synchronized 锁重入:当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。也就是说,在一个synchronized方法/块 的内部调用本类的其他synchronized 方法/块时,是永远可以得到锁的。
上面的例子非常清楚的描述了 可重入锁 的概念:自己可以再次获取自己的内部锁。其实,可重入锁也支持在父子类继承的环境中。
结果说明:当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。
7. 出现异常,锁自动释放
线程a出现异常并释放锁,线程b进入方法正常打印,结论就是:出现异常的锁被自动释放了。
8. 同步不具有继承性
public class Main {
public synchronized void serviceMethod() {
try {
System.out.println("int MAIN 下一步 sleep BEGIN threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("int MAIN 下一步 sleep END threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public String username = "A";
public String password = "AA";
public synchronized void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method threadname = " + Thread.currentThread().getName() + " username = " + username + " password = " + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue() {
System.out.println("getValue method threadname = " + Thread.currentThread().getName() + " username = " + username + " password = " + password);
}
}
public 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"); } }
public class Test { public static void main(String[] args) { try { PublicVar publicVarRef = new PublicVar(); ThreadA threadA = new ThreadA(publicVarRef); threadA.start(); Thread.sleep(200); //打印结果受此值大小影响 System.out.println("main 将要执行 getValue() 方法"); publicVarRef.getValue(); } catch (InterruptedException e) { e.printStackTrace(); } } /* 运行结果: main 将要执行 getValue() 方法 getValue method threadname = main username = B password = AA setValue method threadname = Thread-0 username = B password = BB */ }
解释一下运行结果:线程A调用 publicVar对象的synchronized 的 setValue() 方法,执行完 username 的赋值为 B之后,执行 sleep() 方法休眠5s。这时 main线程执行非 synchronized getValue() 方法,此时 username 已经被线程A赋值为 B了。所以有执行结果:
getValue method threadname = main username = B password = AA线程A休眠结束重新运行,赋值 password= BB,然后输出 username和password。
由此可知,当线程A调用对象Object 的synchronized 方法 X 时,A线程获得了 X 的方法锁,更准确的说是 对象Object锁。所以其他线程想要再执行 X 方法必须等线程A执行完X方法。但是其他线程可以调用对象Object中的其他 非synchronized 同步方法。
为了避免脏读,我们将setValue 也加上synchronized关键字变成同步方法,那么结果就变成:main 将要执行 getValue() 方法
setValue method threadname = Thread-0 username = B password = BB
getValue method threadname = main username = B password = BB
getValue() 方法加上synchronized 之后,main线程调用必须等A线程执行完 setValue() 之后,也就是说 username和password 都已经被重新赋值了。然后main线程才能调用 getValue() 方法,不存在脏读环境。
6. synchronized 锁重入:当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。也就是说,在一个synchronized方法/块 的内部调用本类的其他synchronized 方法/块时,是永远可以得到锁的。
public class Service { public synchronized void service1() { System.out.println("service1"); service2(); } public synchronized void service2() { System.out.println("service2"); service3(); } public synchronized void service3() { System.out.println("service3"); } }
public class MyThread extends Thread { @Override public void run() { Service service = new Service(); service.service1(); } }
public class Test { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } /* 运行结果: service1 service2 service3 */ }
上面的例子非常清楚的描述了 可重入锁 的概念:自己可以再次获取自己的内部锁。其实,可重入锁也支持在父子类继承的环境中。
public class Main { public int i = 10; public synchronized void operateIMainMethod() { try { i--; System.out.println("main print i = " + i); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Sub extends Main { public synchronized void operateISubMethod() { try { while (i > 0) { i--; System.out.println("Sub print i=" + i); Thread.sleep(100); this.operateIMainMethod(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
public class MyThread2 extends Thread { @Override public void run() { Sub sub = new Sub(); sub.operateISubMethod(); } }
public class Test2 { public static void main(String[] args) 4000 { MyThread2 myThread2 = new MyThread2(); myThread2.start(); } /* 运行结果: Sub print i=9 main print i = 8 Sub print i=7 main print i = 6 Sub print i=5 main print i = 4 Sub print i=3 main print i = 2 Sub print i=1 main print i = 0 */ }
结果说明:当存在父子类继承关系时,子类是完全可以通过“可重入锁”调用父类的同步方法的。
7. 出现异常,锁自动释放
public class Service { public synchronized void testMethod() { if (Thread.currentThread().getName().equals("a")) { System.out.println("ThreadName = " + Thread.currentThread().getName() + " run beginTime = " + System.currentTimeMillis()); int i = 1; while (i == 1) { if (("" + Math.random()).substring(0, 8).equals("0.123456")) { System.out.println("ThreadName = " + Thread.currentThread().getName() + " run exceptionTime = " + System.currentTimeMillis()); Integer.parseInt("a"); } } } else { System.out.println("Thread B run Time = " + System.currentTimeMillis()); } } }
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.testMethod(); } }
public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.testMethod(); } }
public class Test { public static void main(String[] args) { try { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("a"); a.start(); Thread.sleep(500); ThreadB b = new ThreadB(service); b.setName("b"); b.start(); } catch (InterruptedException e) { e.printStackTrace(); } } /* 运行结果: ThreadName = a run beginTime = 1506327473193 ThreadName = a run exceptionTime = 1506327473361 Exception in thread "a" java.lang.NumberFormatException: For input string: "a" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:492) at java.lang.Integer.parseInt(Integer.java:527) at com.leo.JavaImprove.chapter2.synchronizedFunc.package217.Service.testMethod(Service.java:15) at com.leo.JavaImprove.chapter2.synchronizedFunc.package217.ThreadA.run(ThreadA.java:16) Thread B run Time = 1506327473693 */ }
线程a出现异常并释放锁,线程b进入方法正常打印,结论就是:出现异常的锁被自动释放了。
8. 同步不具有继承性
public class Main {
public synchronized void serviceMethod() {
try {
System.out.println("int MAIN 下一步 sleep BEGIN threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("int MAIN 下一步 sleep END threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Sub extends Main { public void serviceMethod() { try { System.out.println("int SUB 下一步 sleep BEGIN threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); Thread.sleep(5000); System.out.println("int SUB 下一步 sleep END threadName = " + Thread.currentThread().getName() + " time = " + System.currentTimeMillis()); super.serviceMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class ThreadA extends Thread { private Sub sub; public ThreadA(Sub sub) { super(); this.sub = sub; } @Override public void run() { sub.serviceMethod(); } }
public class ThreadB extends Thread { private Sub sub; public ThreadB(Sub sub) { super(); this.sub = sub; } @Override public void run() { sub.serviceMethod(); } }
public class Test { public static void main(String[] args) { Sub subRef = new Sub(); ThreadA a = new ThreadA(subRef); a.setName("A"); a.start(); ThreadB b = new ThreadB(subRef); b.setName("B"); b.start(); } /* 运行结果: int SUB 下一步 sleep BEGIN threadName = A time = 1506330748908 } int SUB 下一步 sleep BEGIN threadName = B time = 1506330748909 } 线程A、B异步执行了 sub方法,BEGIN 输出 int SUB 下一步 sleep END threadName = A time = 1506330753909 int MAIN 下一步 sleep BEGIN threadName = A time = 1506330753909 int SUB 下一步 sleep END threadName = B time = 1506330753910 int MAIN 下一步 sleep END threadName = A time = 1506330758909 int MAIN 下一步 sleep BEGIN threadName = B time = 1506330758909 int MAIN 下一步 sleep END threadName = B time = 1506330763910 ------sub.serviceMethod() 方法添加 synchronized 后---- int SUB 下一步 sleep BEGIN threadName = A time = 1506330882904 } int SUB 下一步 sleep END threadName = A time = 1506330887904 } 同步执行,BEGIN、END 交替输出 int MAIN 下一步 sleep BEGIN threadName = A time = 1506330887904 int MAIN 下一步 sleep END threadName = A time = 1506330892905 int SUB 下一步 sleep BEGIN threadName = B time = 1506330892905 int SUB 下一步 sleep END threadName = B time = 1506330897905 int MAIN 下一步 sleep BEGIN threadName = B time = 1506330897905 int MAIN 下一步 sleep END threadName = B time = 1506330902906 */ }
相关文章推荐
- 第二章、对象及变量的并发访问 之 synchronized 同步方法(1)
- java多线程之-----对象及变量的并发访问1(synchronized同步方法)
- synchronized关键字修饰对象锁,同步与非同步对该方法的访问及修改
- 第二章:对象及变量的并发访问
- java多线程基础值对象和变量的并发访问之synchronized(二)
- JAVA多线程-对象及变量的并发访问(一)synchronized详解
- 对象及变量的并发访问——Synchronized详解
- 第二章 对象及变量的并发访问
- 多线程编程核心技术读书笔记(二):对象及变量的并发访问(synchronized关键字)
- 通过操作地址的方法来访问C++类对象中私有成员变量
- JavaSE第六十四讲:使用反射机制调用对象的私有方法、访问对象的私有成员变量
- 访问C++类对象中私有成员变量的方法
- OC中继承子类对象调用方法机制 子类对象访问父类中的实例变量
- 《Java多线程编程核心技术》--第2章--对象及变量的并发访问
- jsp:synchronized方法控制对类成员变量的访问(实例)
- 同时创建多个线程,它们都访问一个对象中的同步方法,怎么确保第一个线程最先拿到对象锁,否则,怎么保证多个线程之间的有序通信
- 多线程数据同步之synchronized同步方法-多线程访问临界资源
- 多线程编程学习二(对象及变量的并发访问)
- 另一种方法是使用锁来同步访问对象
- 访问C++类对象中私有成员变量的方法