对象锁的同步与异步
同步:synchronized
同步的概念就是共享 , 如果不是共享的资源 , 就没有必要进行同步。
异步:asynchronized
异步的概念就是独立 , 相互之间不受到任何制约。
同步的目的就是为了线程安全 , 其实对于线程安全来说 , 需要满足两个特性:
- 原子性 (同步):同步性就是一个事物要么一起成功,要么一起失败。
- 可见性:就是一个线程的操作可以及时被其他线程感知到。
1 package com.itdoc.multi.sync003; 2 3 /** 4 * 对象锁的同步与异步 5 * 6 * @author Wáng Chéng Dá 7 * @create 2017-03-20 11:36 8 */ 9 public class MyObject { 10 11 public synchronized void method1() { 12 try { 13 System.out.println(Thread.currentThread().getName()); 14 Thread.sleep(5000); 15 } catch (InterruptedException e) { 16 e.printStackTrace(); 17 } 18 } 19 20 public synchronized void method2() { 21 22 try { 23 System.out.println(Thread.currentThread().getName()); 24 Thread.sleep(1000); 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 } 29 30 public static void main(String[] args) { 31 final MyObject mo = new MyObject(); 32 33 /** 34 * 分析: 35 * t1 线程先持有对象锁, t2 线程可以以异步的方式调用对象中的非 synchronized 修饰的方法。 36 * t1 线程现持有对象锁, t2 线程若是要调用对象中的同步方法 (synchronized 修饰的方法), 37 * 需要等 t1 线程之行结束将对象锁释放后才开始执行 (同步)。 38 * 若是静态方法 .class 类锁, 效果一样。 39 * 注意: 线程之间必须是相同的锁才可谈论异步同步问题。 40 */ 41 Thread t1 = new Thread(new Runnable() { 42 @Override 43 public void run() { 44 mo.method1(); 45 } 46 }, "T1"); 47 48 Thread t2 = new Thread(new Runnable() { 49 @Override 50 public void run() { 51 mo.method2(); 52 } 53 }, "T2"); 54 t1.start(); 55 t2.start(); 56 } 57 }
脏读:
1 package com.itdoc.multi.sync004; 2 3 /** 4 * 业务整体需要使用完整的 synchronized, 保持业务的原子性。 5 * 6 * @author Wáng Chéng Dá 7 * @create 2017-03-20 13:52 8 */ 9 public class DirtyRead { 10 11 private String username = "z3"; 12 13 private String password = "123"; 14 15 public synchronized void setValue(String username, String password) { 16 this.username = username; 17 try { 18 Thread.sleep(4000); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 this.password = password; 23 System.out.println("setValue 最终结果: username = " + username + " -- password = " + password); 24 } 25 26 public synchronized void getValue() { 27 System.out.println("getValue 方法得到: username = " + this.username + " -- password = " + this.password); 28 } 29 30 public static void main(String[] args) throws InterruptedException { 31 final DirtyRead dirtyRead = new DirtyRead(); 32 Thread t1 = new Thread(new Runnable() { 33 @Override 34 public void run() { 35 dirtyRead.setValue("z3", "456"); 36 } 37 }, "T1"); 38 t1.start(); 39 Thread.sleep(1000); 40 dirtyRead.getValue(); 41 } 42 }
若方法不同步 , 在 t1 线程执行睡眠时, 主线程已经执行完成 , 并打印出 getValue 方法得到: username = z3 -- password = 123 这种数据 , 只有方法同步 , 才能保持业务的原子性。