单例模式(饿汉式)
/*饿汉式由jvm来保证一个类的<cintit>方法在多线程环境下被正确地加锁同步,如果多个线程同时去初始化一个类,那么只会有其中一个线程去执行这个类的<cinit>方法,其他线程都需要阻塞等待,直到活动线程执行完毕。如果一个类的<cinit>方法执行耗时太长,可能造成多个线程阻塞等待。*/public class SingtonHungry {public SingtonHungry() {}private static SingtonHungry instance=new SingtonHungry();public static SingtonHungry getInstance(){return instance;}}
单例模式(懒汉式)
/*如果abc三个线程同时调用静态的getInstance(),会造成多个线程获得不同的instance实例(分配的内存地址不同),这就有违单例模式了,所以这是有问题的。*/public class SingletonLazy {private static SingletonLazy instance=null;private static SingletonLazy getInstance(){if(instance==null){instance=new SingletonLazy();}return instance;}}
改进1(synchronized加锁)
/*使用synchronized锁来保护临界区共享资源*/public class SingletonLazy {private static SingletonLazy instance=null;private static SingletonLazy getInstance(){synchronized (SingletonLazy.class) {/*这里是线程安全的,因为每次进来都需要加锁。打个比方,ab线程同时进来,a线程进入monitorEnter的逻辑,b线程进入entryList阻塞队列去等待锁释放,a线程初始化完instance后,进入monitorExit逻辑,释放锁。b线程还需要再次判断instance是否为null。*/if(instance==null) {instance = new SingletonLazy();}}//缺点就是,不管instance是否初始化完成,后续的每个线程进来获取instance实例都需要加锁,效率太低。return instance;}}
改进2(双重检查锁)
/*双重检查锁*/public class SingletonLazy {public SingletonLazy() {}private static SingletonLazy instance=null;private static SingletonLazy getInstance(){/*这样看似已经做到线程安全了,并且后续线程进来只需判断instance是否为null,发现不为null直接返回即可。但还是存在问题的实际上。*//*分析:第一重检查的instance==null的逻辑判断是没有加锁的,并且也没有使用volatile关键字来修饰。所以极端情况:a、b两个线程同时进入第一重检查,都判断instance==null,而a线程加锁成功;b阻塞等待。a线程进入第二重检查发现instance==null,所以会执行instance==new SingletonLazy()的逻辑。这个语句有两个逻辑,(1)内存分配;(2)执行init(),就是对象的构造方法;(3)静态变量的指针指向堆中的对象所以这种情况可能会导致指令重排序,将堆中的对象的地址先赋值给静态变量,再调用init();如果此时有c线程进来,拿到的就是一个不完整的instance实例对象。*/if(instance==null) {synchronized (SingletonLazy.class) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}}
改进3(volatile关键字禁止指令重排序)
/*双重检查锁+volatile关键字*/public class SingletonLazy {public SingletonLazy() {}private static volatile SingletonLazy instance=null;private static SingletonLazy getInstance(){/*volatile关键字能保证共享变量在多线程之间的可见性与有序性。原理是内存屏障:storeStore屏障保证其之前的所有的读写指令执行完,不与volatile域写重排序;storeLoad屏障保证volatile域写后将高速缓存中的共享变量强制刷新到内存中,不能等到高速缓存满再刷脏。volatile域读会在前后施加loadLoad和loadStore两个屏障,loadLoad屏障保证volatile域读共享变量一定到内存中读取,让自己高速缓存的共享变量值无效;loadStore屏障保证volatile域读后下面所有的读写操作不与它重排序*/if(instance==null) {synchronized (SingletonLazy.class) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}}
奇偶线程交替打印1-100
方法一:ReentrantLock
/*odd线程打印奇数、even线程打印偶数方法一:使用锁机制*/import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class oneLock {private static ReentrantLock lock=new ReentrantLock();private static int count=1;private static Condition oddCondition;private static Condition evenCondition;public static void main(String[] args) throws InterruptedException {oddCondition=lock.newCondition();evenCondition=lock.newCondition();new Thread(new Runnable() {@Overridepublic void run() {while (count<100){lock.lock();System.out.println(Thread.currentThread().getName()+"打印了"+count++);evenCondition.signalAll();try {oddCondition.await();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}//这儿退出while循环后需要让奇线程获取锁,让偶线程别wait了,要不然程序一直无法退出,//奇线程先拿到锁后再唤醒偶线程,最后释放锁if(count>100) {lock.lock();evenCondition.signalAll();lock.unlock();}}},"奇线程").start();//main线程在调用.start()启动后,先睡10毫秒,让奇线程一定先于偶线程执行;Thread.sleep(10);new Thread(new Runnable() {@Overridepublic void run() {while (count<=100){lock.lock();System.out.println(Thread.currentThread().getName()+"打印了"+count++);oddCondition.signalAll();try {evenCondition.await();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}},"偶线程").start();}}
方法二:volatile关键字修饰flag标志位+原子整数(cas)
//cpu空转就完事了啊import java.util.concurrent.atomic.AtomicInteger;public class twoVolatile {private static volatile boolean flag=true;private static AtomicInteger count=new AtomicInteger(1);public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while (count.get()<100){if(flag){System.out.println(Thread.currentThread().getName()+"打印了"+count.getAndIncrement());flag=false;}}}},"奇线程").start();new Thread(new Runnable() {@Overridepublic void run() {while (count.get()<=100){if(!flag){System.out.println(Thread.currentThread().getName()+"打印了"+count.getAndIncrement());flag=true;}}}},"偶线程").start();}}
方法三:信号量Semaphore
/*odd线程打印奇数、even线程打印偶数方法三:使用信号量因为最后一个打印的数是偶数,所以奇线程阻塞等待的时候acquire(),因为他在99的时候判断了是小于等于100的,然后再++成100,然后再进入循环发现还是小于等于100.所以才能进入acuire()中,所以如果奇线程的方法执行的while(count<=100)它会打印101。*/public class threeSemaphore {private static Semaphore oddSemaphore=new Semaphore(1);private static Semaphore evenSemaphore=new Semaphore(0);private static int count=1;public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while (count<100) {try {oddSemaphore.acquire();System.out.println(Thread.currentThread().getName()+"打印了"+count++);evenSemaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}}}, "奇线程").start();new Thread(new Runnable() {@Overridepublic void run() {while (count<=100) {try {evenSemaphore.acquire();System.out.println(Thread.currentThread().getName()+"打印了"+count++);oddSemaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}}}, "偶线程").start();}}
三个窗口卖100张票(防止超卖)
方法一:加锁
import java.util.concurrent.locks.ReentrantLock;/*这里三个线程的代码都是一样的,或者直接用Runnable来创建任务即可,然后创建三个线程一起执行这个任务。每个线程睡上10毫秒,是为了防止一个窗口把票卖完*/public class oneLock {private static int count=100;private static ReentrantLock lock=new ReentrantLock();public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while (count>=1) {try {Thread.sleep(10);lock.lock();if (count >= 1) {System.out.println(Thread.currentThread().getName() + "售出了第" + count-- + "张票");} else {break;}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}},"窗口二").start();new Thread(new Runnable() {@Overridepublic void run() {while (count>=1) {try {Thread.sleep(10);lock.lock();if (count >= 1) {System.out.println(Thread.currentThread().getName() + "售出了第" + count-- + "张票");} else {break;}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}},"窗口三").start();new Thread(new Runnable() {@Overridepublic void run() {while (count>=1) {try {Thread.sleep(10);lock.lock();if (count >= 1) {System.out.println(Thread.currentThread().getName() + "售出了第" + count-- + "张票");} else {break;}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}}},"窗口一").start();}}
方法二:原子整数
import java.util.concurrent.atomic.AtomicInteger;public class caseTwo {private static AtomicInteger count=new AtomicInteger(100);public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while (count.get()>=1){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售出了"+count.getAndDecrement()+"张票");}}}, "窗口一").start();new Thread(new Runnable() {@Overridepublic void run() {while (count.get()>=1){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售出了"+count.getAndDecrement()+"张票");}}}, "窗口二").start();new Thread(new Runnable() {@Overridepublic void run() {while (count.get()>=1){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"售出了"+count.getAndDecrement()+"张票");}}}, "窗口三").start();}}
