方法私有变量是线程安全的
线程是否安全的关键问题在于实例中的变量
如果变量是方法的私有变量,则是线程安全的
package com.test;
import com.myObject.Object1;
import com.myThread.Thread1a;
import com.myThread.Thread1b;
public class Test1 {
public static void main(String[] args) throws InterruptedException {
// 测试1
Object1 object1 = new Object1();
Thread1a thread1a = new Thread1a(object1);
thread1a.start();
Thread1b thread1b = new Thread1b(object1);
thread1b.start();
}
}
package com.myThread;
import com.myObject.Object1;
public class Thread1a extends Thread {
Object1 object1;
public Thread1a(Object1 object1) {
this.object1 = object1;
}
@Override
public void run() {
super.run();
object1.printI(404);
}
}
package com.myThread;
import com.myObject.Object1;
public class Thread1b extends Thread {
Object1 object1;
public Thread1b(Object1 object1) {
this.object1 = object1;
}
@Override
public void run() {
super.run();
object1.printI(500);
}
}
package com.myObject;
public class Object1 {
String msg;
public void printI(int i) {
if (i == 404) {
msg = "找不到请求资源";
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (i == 500)
msg = "服务器出错";
System.out.println(i + ":" + msg);
}
}
打印结果
500:服务器出错
404:服务器出错
若将Object1 的全局变量String msg放到printI(int i)方法内或者使用关键字synchronized,则是线程安全的
打印结果会一直为
500:服务器出错
404:找不到请求资源
synchronized取得的是对象锁
1)关键字synchronized取得的是对象的锁,而不是把一段代码块或者方法当做锁
2)哪个线程先执行带有synchronized关键字的方法,哪个线程就获得该对象的锁
3)没有获得对象锁的线程,能执行同一对象并且没有上对象锁的方法即没有synchronized关键字的方法
package com.test;
import com.myObject.Object2;
import com.myThread.Thread2a;
import com.myThread.Thread2b;
public class Test2 {
public static void main(String[] args) throws InterruptedException {
// 测试1
Object2 object2 = new Object2();
Thread2a thread2a = new Thread2a(object2);
thread2a.setName("A");
Thread2b thread2b = new Thread2b(object2);
thread2b.setName("B");
thread2a.start();
thread2b.start();
}
}
package com.myThread;
import com.myObject.Object2;
public class Thread2a extends Thread {
Object2 object2;
public Thread2a(Object2 object2) {
this.object2 = object2;
}
@Override
public void run() {
super.run();
object2.mothdA();
}
}
package com.myThread;
import com.myObject.Object2;
public class Thread2b extends Thread {
Object2 object2;
public Thread2b(Object2 object2) {
this.object2 = object2;
}
@Override
public void run() {
super.run();
object2.mothdB();
}
}
package com.myObject;
public class Object2 {
synchronized public void mothdA() {
System.out.println("begin mothdA");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.out.println("end mothdA");
}
public void mothdB() {
System.out.println("begin mothdB");
System.out.println(Thread.currentThread().getName());
System.out.println("end mothdB");
}
}
打印结果
begin mothdA
begin mothdB
B
end mothdB
A
end mothdA
分析:mothdA是上了对象锁的,而mothdB没有,所以当别的线程访问同一个对象时,没有同步的方法都可以执行
若将mothdB加上关键字synchronized
打印结果
begin mothdA
A
end mothdA
begin mothdB
B
end mothdB
小结:
1)A线程先取得Object的Lock,则B线程还是可以异步调用Object中没有sychronized关键字的方法
2)A线程先取得Object的Lock,则B线程调用Object中任何sychronized关键字的方法都需要等待
脏读
既然A线程先取得Object的Lock,则B线程还是可以异步调用Object中没有sychronized关键字的方法,那么若对象的读取实例变量的方法没有使用synchronized,则有可能出现脏读
synchronized锁重入
1)Object对象的mothdA()和mothdB()都是带synchronized
2)ThreadA调用mothdA(),而mothdA()调用mothdB(),ThreadA还是能得到Object的锁
package com.test;
import com.myObject.Object3;
public class Test3 {
public static void main(String[] args) {
Thread thread =new Thread(){
@Override
public void run() {
super.run();
Object3 object3 =new Object3();
object3.mothdA();
}
};
thread.start();
}
}
package com.myObject;
public class Object3 {
synchronized public void mothdA() {
System.out.println(" mothdA");
mothdB();
}
synchronized public void mothdB() {
System.out.println(" mothdB");
}
}
打印结果
mothdA
mothdB
分析
可重入锁就是在某线程获得某个对象的锁,然后在释放改锁之前,想再次获取还是可以获取的。
注意:锁的重入也支持继承环境,如下伪代码
class A{
synchronized publish void mothdA(){
...
}
}
class B extends A{
synchronized publish void mothdB(){
...
this.mothdA();
}
}
class Test{
main(){
Thread thread =new Thread(){
@Override
public void run() {
super.run();
B b=new B();
b.mothdB();
}
};
thread.start();
}
}
异常会释放锁
package com.myObject;
import java.io.IOException;
public class Object4 {
synchronized public void exceptionMothd() {
if("a".equals(Thread.currentThread().getName())){
try {
System.out.println("Thread A");
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int i=1/0;
}
else {
System.out.println("Thread B");
}
}
}
package com.test;
import com.myObject.Object4;
public class Test4 {
public static void main(String[] args) throws InterruptedException {
final Object4 object4 = new Object4();
Thread threadA = new Thread() {
@Override
public void run() {
super.run();
object4.exceptionMothd();
}
};
Thread threadB = new Thread() {
@Override
public void run() {
super.run();
object4.exceptionMothd();
}
};
threadA.setName("a");
threadB.setName("b");
threadA.start();
Thread.sleep(3000);
threadB.start();
}
}
打印结果
Thread A
Exception in thread "a" Thread B
java.lang.ArithmeticException: / by zero
at com.myObject.Object4.exceptionMothd(Object4.java:15)
at com.test.Test4$1.run(Test4.java:13)
线程A出现了异常释放了锁,然后线程B获得锁进入方法,进行打印