您的位置:首页 > 其它

第二章、对象及变量的并发访问 之 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);
}
}
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
相关文章推荐