一、保护共享资源-加锁实现
synchronized加锁实现,悲观锁
jdk8之后加入的新特性:接口中可以创建实现的静态方法,可以拿接口引用对象比实现接口的实现类对象
import java.sql.SQLOutput;import java.util.ArrayList;import java.util.List;public class TestAccount {public static void main(String[] args) {AccountUnsafe account = new AccountUnsafe(10000);Account.demo(account);}}class AccountUnsafe implements Account{private Integer balance;public AccountUnsafe(Integer balance){this.balance=balance;}@Overridepublic synchronized Integer getBalance() {return balance;}@Overridepublic synchronized void withdraw(Integer amout) {balance-=amout;}}interface Account{//获取余额Integer getBalance();//取款void withdraw(Integer amout);/** 方法内会启动1000个线程,每个线程作 -10元 的操作* 如果初始余额为 10000 那么线程正确执行的结果应为0元* *//**jdk1.8之后支持接口中定义并实现静态方法* 本方法即1000个线程去一个银行对象取钱,1000个线程去调用getBalance方法与withdraw方法* */static void demo(Account account){List<Thread> ts = new ArrayList<>();//创建线程集合for (int i=0;i<1000;i++){ts.add(new Thread(()->{account.withdraw(10);}));}long start = System.nanoTime();ts.forEach(Thread::start);//每个线程执行start方法ts.forEach(t->{try{t.join();}catch (InterruptedException e){e.printStackTrace();}});long end= System.nanoTime();System.out.println(account.getBalance()+" cost:"+(end-start)/1000_000+"ms");}}
注意AccountUnsafe类的两个实现访问均涉及对共享变量的访问,所以当多个线程调用这两个方法的时候,要加上synchronized保护。
二、保护共享资源-无锁实现
完全无锁实现多线程并发,CAS与volatile结合实现,乐观锁
余额balance对象改成AtomicInteger类(原子整数类)的对象。
import java.sql.SQLOutput;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;public class TestAccount {public static void main(String[] args) {AccountCas account = new AccountCas(10000);Account.demo(account);}}class AccountCas implements Account{private AtomicInteger balance;public AccountCas(int balance){this.balance=new AtomicInteger(balance);}@Overridepublic Integer getBalance() {return balance.get();}@Overridepublic void withdraw(Integer amout) {while(true){int prev=balance.get();//获取balance的最新值//要修改后的余额int next=prev-amout;//真正修改if(balance.compareAndSet(prev,next)){break;}//修改前和修改后的值传入}}}interface Account{//获取余额Integer getBalance();//取款void withdraw(Integer amout);/** 方法内会启动1000个线程,每个线程作 -10元 的操作* 如果初始余额为 10000 那么线程正确执行的结果应为0元* *//**jdk1.8之后支持接口中定义并实现静态方法* 本方法即1000个线程去一个银行对象取钱,1000个线程去调用getBalance方法与withdraw方法* */static void demo(Account account){List<Thread> ts = new ArrayList<>();//创建线程集合for (int i=0;i<1000;i++){ts.add(new Thread(()->{account.withdraw(10);}));}long start = System.nanoTime();ts.forEach(Thread::start);//每个线程执行start方法ts.forEach(t->{try{t.join();}catch (InterruptedException e){e.printStackTrace();}});long end= System.nanoTime();System.out.println(account.getBalance()+" cost:"+(end-start)/1000_000+"ms");}}
三、无锁效率更高的原因
- 无锁状况下,即使重试失败,线程始终在高速运行,仍处于运行状态,没有停歇,而synchronized会让线程在没有获得锁的时候,发生线程上下文切换,进入阻塞,将一些线程信息保存起来,比较耗费资源,打个比方:
 - 线程好比高速跑道上的赛车,高速运行时,速度超快,一旦发生上下文切换,就好比赛车要减速、熄火等被唤醒又需要重新打火、启动、加速….代价较大
 - 但无锁状态下,因为线程要保持运行,需要额外CPU的支持,因为需要保持线程处于自旋状态,CPU在这里好比高速跑道,没有额外的跑道,线程想高速运行也无从谈起,虽然不会进入阻塞,但由于没有分到时间片,仍然会进入可运行状态,还是会导致上下文切换。
 
总结:无锁是线程均运行但是条件不满足就不会改变共享变量,有锁是没获得锁的线程直接阻塞住,只有拿到锁的线程才有资格对共享变量作读写。
