非线程安全问题
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程问题”。也即是说,方法中的变量永远是线程安全的。
如果多个线程共同访问1个对象中的实例变量,则可能线程不安全。下面以实例说明
1 public class HasSelfNum { 2 private int num = 0; 3 public void add(String name) { 4 try { 5 if (name.equals("a")) { 6 num = 100; 7 System.out.println("a over"); 8 Thread.sleep(1000); 9 } else { 10 num = 200; 11 System.out.println("b over"); 12 } 13 System.out.println(name+" "+num); 14 } catch (InterruptedException e) { 15 // TODO Auto-generated catch block 16 e.printStackTrace(); 17 } 18 } 19 }
1 public class ThreadA extends Thread{ 2 private HasSelfNum hasSelfNum; 3 4 public ThreadA(HasSelfNum hasSelfNum) { 5 this.hasSelfNum = hasSelfNum; 6 } 7 8 @Override 9 public void run() { 10 super.run(); 11 hasSelfNum.add("a"); 12 } 13 }
1 public class ThreadB extends Thread{ 2 private HasSelfNum hasSelfNum; 3 4 public ThreadB(HasSelfNum hasSelfNum) { 5 this.hasSelfNum = hasSelfNum; 6 } 7 8 @Override 9 public void run() { 10 super.run(); 11 hasSelfNum.add("b"); 12 } 13 }
1 public class Main { 2 public static void main(String[] args) { 3 HasSelfNum hasSelfNum = new HasSelfNum(); 4 ThreadA threadA = new ThreadA(hasSelfNum); 5 ThreadB threadB = new ThreadB(hasSelfNum); 6 7 threadA.start(); 8 threadB.start(); 9 } 10 }
Result
1 a over 2 b over 3 b 200 4 a 200
非线程安全!
如何处理?
在add方法上加上关键字 synchronized
synchronized public void add(String name)
Result
1 a over 2 a 100 3 b over 4 b 200
线程安全,同步访问add()方法
如果是多个对象的情况?
将上面的Main.java进行修改
1 public class Main { 2 public static void main(String[] args) { 3 HasSelfNum hasSelfNum = new HasSelfNum(); 4 HasSelfNum hasSelfNum2 = new HasSelfNum(); //两个对象 5 6 ThreadA threadA = new ThreadA(hasSelfNum); 7 ThreadB threadB = new ThreadB(hasSelfNum2); 8 9 threadA.start(); 10 threadB.start(); 11 } 12 }
Result
1 a over 2 b over 3 b 200 4 a 100
两个线程分别访问同一个类的不同实例的相同同步方法,产生了两个锁,运行的结果是异步的。
由此可以看出,关键字synchronized取得的锁是对象锁
若类中既有synchronized类型方法又有非synchronized类型方法
1 public class MyObject { 2 synchronized public void methodA(){ 3 try { 4 System.out.println(Thread.currentThread().getName()+" begin"); 5 Thread.sleep(1000); 6 System.out.println(Thread.currentThread().getName()+" end"); 7 } catch (InterruptedException e) { 8 // TODO Auto-generated catch block 9 e.printStackTrace(); 10 } 11 } 12 13 public void methodB(){ 14 try { 15 System.out.println(Thread.currentThread().getName()+" begin"); 16 Thread.sleep(1000); 17 System.out.println(Thread.currentThread().getName()+" end"); 18 } catch (InterruptedException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 } 23 24 }
1 public class ThreadA extends Thread{ 2 MyObject myObject; 3 public ThreadA(MyObject myObject) { 4 this.myObject = myObject; 5 } 6 7 @Override 8 public void run() { 9 super.run(); 10 myObject.methodA(); 11 } 12 }
1 public class ThreadB extends Thread{ 2 MyObject myObject; 3 public ThreadB(MyObject myObject) { 4 this.myObject = myObject; 5 } 6 7 @Override 8 public void run() { 9 super.run(); 10 myObject.methodB(); 11 } 12 }
1 public class Main { 2 public static void main(String[] args) { 3 MyObject myObject = new MyObject(); 4 5 ThreadA threadA = new ThreadA(myObject); 6 ThreadB threadB = new ThreadB(myObject); 7 threadA.setName("A"); 8 threadB.setName("B"); 9 10 threadA.start(); 11 threadB.start(); 12 } 13 }
Result
1 A begin 2 B begin 3 B end 4 A end
线程A持有myObject对象锁,但线程B仍可以异步调用非synchronized类型方法
当在methodB方法前也加上synchronized关键字时
Result
1 A begin 2 A end 3 B begin 4 B end
线程A、B以同步的方式执行对象中的两方法
synchronized同步代码块
当synchronized修饰的方法里有个耗时很长的代码时,效率是很低的。同步代码块分为:
1.synchronized(this)
在方法中不在同步代码块中的代码异步执行,而在其中的代码同步执行
同个对象中的synchronized(this)使用的对象监视器是同一个,也就是说,当一个线程访问某个synchronized(this)代码块里面的方法时,其他线程访问其他synchronized(this)块里面的方法是会被阻塞的。另外synchronized(this)使用的监视器和syncheronized 修饰方法一致是当前对象,也会阻塞synchronized修饰的方法。
2.synchronized(obj) 非this
多个线程同时访问时只能阻塞synchronized(obj)的代码块。
同时如果锁是同一个myObject对象,还是会阻塞的
1 public class MyObject { 2 synchronized public void methodA() { 3 try { 4 System.out.println(Thread.currentThread().getName() + " begin"); 5 Thread.sleep(1000); 6 System.out.println(Thread.currentThread().getName() + " end"); 7 } catch (InterruptedException e) { 8 // TODO Auto-generated catch block 9 e.printStackTrace(); 10 } 11 } 12 public void methodB(MyObject myObject) { 13 synchronized (myObject) { 14 try { 15 System.out.println(Thread.currentThread().getName() + " begin"); 16 Thread.sleep(1000); 17 System.out.println(Thread.currentThread().getName() + " end"); 18 } catch (InterruptedException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 } 23 } 24 }
1 public class ThreadA extends Thread{ 2 MyObject myObject; 3 public ThreadA(MyObject myObject) { 4 this.myObject = myObject; 5 } 6 7 @Override 8 public void run() { 9 super.run(); 10 myObject.methodA(); 11 } 12 }
1 public class ThreadB extends Thread{ 2 MyObject myObject; 3 public ThreadB(MyObject myObject) { 4 this.myObject = myObject; 5 } 6 7 @Override 8 public void run() { 9 super.run(); 10 myObject.methodB(myObject); 11 } 12 }
1 public class Main { 2 public static void main(String[] args) { 3 MyObject myObject = new MyObject(); 4 5 ThreadA threadA = new ThreadA(myObject); 6 ThreadB threadB = new ThreadB(myObject); 7 threadA.setName("A"); 8 threadB.setName("B"); 9 10 threadB.start(); 11 threadA.start(); 12 } 13 }
Result
1 B begin 2 B end 3 A begin 4 A end
《Java多线程编程核心技术》学习