当我们对一个数字进行递增操作时,如果两个程序同时访问,第一个线程读到count=0,并对其+1,在自己线程内部的内存里还没有写回去的时候;第二个线程读到的count也是0,并+1写回去;但是程序明明对count进行了两次+1操作,但结果还是1。 那么我们对这个递增过程加上一把锁,当第一个程序访问的时候,这个资源是它独占的,不允许别的线程访问计算,当第一个线程计算完成并释放锁之后其它线程才能访问,这样就保证了线程安全。
public class Thread_006 { private static int count = 0; public static void main(String[] args) { new Thread(()->{ countAdd(); }).start(); new Thread(()->{ countAdd(); }).start(); } //去掉synchronized 可以看出来不加锁的情况下我们预期的结果与实际结果是不符合的 static /*synchronized*/ void countAdd(){ for (int i = 0; i < 100; i++) { try { //相当于你线程处理的业务 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } count++; } System.out.println(Thread.currentThread().getName()+"count = "+ count); } } 当不在countAdd方法上加锁时的执行结果: Thread-1count = 155 Thread-0count = 155 当在countAdd方法上加锁的执行结果: Thread-0count = 100 Thread-1count = 200面试题:模拟银行账户,对业务写方法加锁,对业务读方法不加锁,可以吗? 答:不可以,容易产生脏读现象;具体看代码,解决方法就是把读方法也加锁
public class Thread_010 { String name; double balance; public /*synchronized*/ void set(String name,double balance){ this.name = name; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.balance = balance; } public synchronized double getBalance(){ return this.balance; } public static void main(String[] args) { Thread_010 account = new Thread_010(); new Thread(()->account.set("柯南",300.0)).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("当前账户余额:"+account.getBalance()); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("当前账户余额:"+account.getBalance()); } } 不加锁执行结果: 当前账户余额:0.0 当前账户余额:300.0 加锁执行结果: 当前账户余额:300.0 当前账户余额:300.0面试题:CAS(自旋锁)一定比系统锁的效率高吗? 答:不一定,分具体情况:执行时间短(加锁的代码),线程数少,用自旋;执行时间长,线程数多,用系统锁。