方法中的变量不存在非线程安全问题,永远是线程安全的。这是方法内部的变量是私有的特性造成的。
如果多个线程共同访问1个对象中的实例变量,则有可能出现“非线程安全”问题。
关键字synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁,其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。但如果多个线程访问多个对象,则JVM会创建多个锁。
多个线程访问一个对象——》创建一个对象锁
多个线程访问多个对象——》创建多个对象锁
1 ) A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object对象中的 非synchronized类型的方法。
2 ) A线程先持有object对象的Lock锁,B线程如果在这时调用object对象中的synchronized类型的方法则需等待,也就是同步。
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。
“可重入锁”的概念是:自己可以再次获取自己的内部锁。 比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。
1 public class Service { 2 synchronized public void service1(){ 3 System.out.println("service1"); 4 service2(); 5 } 6 synchronized public void service2(){ 7 System.out.println("service2"); 8 service3(); 9 } 10 synchronized public void service3(){ 11 System.out.println("service3"); 12 } 13 }
1 public class MyThread extends Thread { 2 3 public static void main(String[] args) { 4 (new MyThread()).start(); 5 } 6 7 @Override 8 public void run() { 9 super.run(); 10 Service service = new Service(); 11 service.service1(); 12 13 } 14 }
同步不具备继承,所以还得在子类的方法中添加synchronized关键字
1)用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间。在这样的情况下可以使用 synchronized同步语句块来解决.
2)当两个并发线程访问同一个对象中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
3)不在synchronized块中就是异步执行,在synchronized块中就是同步执行。
4)和synchronized方法一样,synchronized(this)代码块也是锁定当前对象的。
多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
1 )对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
2 )同一时间只有一个线程可以执行synchronized同步方法中的代码。
1 )对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
2 )同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码。
Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。
1)在多个线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
2)当持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码。
优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则
synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高运行效率。
使用“ synchronized(非this对象x) 同步代码块”格式进行同步操作时,对象监视器必须是同一个对象。如果不是同一个对象监视器,运行的结果就是异步调用了,就会交叉运行。
1 public class UserService { 2 private String username; 3 private String password; 4 private String str = new String(); 5 public void set(String username, String password) { 6 7 synchronized (str) { 8 try { 9 System.out.println(Thread.currentThread().getName() + ":begin:" + System.currentTimeMillis()); 10 this.username = username; 11 Thread.sleep(3000); 12 System.out.println(Thread.currentThread().getName() + ":end:" + System.currentTimeMillis()); 13 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 } 18 } 19 }
1 public class ThreadA extends Thread{ 2 private UserService userService; 3 4 public ThreadA(UserService userService) { 5 this.userService = userService; 6 } 7 8 @Override 9 public void run() { 10 userService.set("a","aa"); 11 } 12 } 13 14 15 public class ThreadB extends Thread{ 16 private UserService userService; 17 18 public ThreadB(UserService userService) { 19 this.userService = userService; 20 } 21 22 @Override 23 public void run() { 24 userService.set("b","bb"); 25 } 26 }
1 public class Run { 2 public static void main(String[] args) { 3 4 UserService userService = new UserService(); 5 ThreadA a = new ThreadA(userService); 6 a.setName("A"); 7 a.start(); 8 ThreadB b = new ThreadB(userService); 9 b.setName("B"); 10 b.start(); 11 } 12 }
更改UserService.java的代码,在set方法中创建str对象。同步代码块异步执行了,因为两个线程获得的对象锁不是同一个。
1 public class UserService { 2 private String username; 3 private String password; 4 5 public void set(String username, String password) { 6 String str = new String(); 7 synchronized (str) { 8 try { 9 System.out.println(Thread.currentThread().getName() + ":begin:" + System.currentTimeMillis()); 10 this.username = username; 11 Thread.sleep(3000); 12 System.out.println(Thread.currentThread().getName() + ":end:" + System.currentTimeMillis()); 13 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 } 18 } 19 }
原文:https://www.cnblogs.com/le-le/p/12061549.html