单例模式(饿汉式)

  1. /*
  2. 饿汉式由jvm来保证一个类的<cintit>方法在多线程环境下被正确地加锁同步,如果多个线程同时去初始化一个类,
  3. 那么只会有其中一个线程去执行这个类的<cinit>方法,其他线程都需要阻塞等待,直到活动线程执行完毕。
  4. 如果一个类的<cinit>方法执行耗时太长,可能造成多个线程阻塞等待。
  5. */
  6. public class SingtonHungry {
  7. public SingtonHungry() {
  8. }
  9. private static SingtonHungry instance=new SingtonHungry();
  10. public static SingtonHungry getInstance(){
  11. return instance;
  12. }
  13. }

单例模式(懒汉式)

  1. /*
  2. 如果abc三个线程同时调用静态的getInstance(),会造成多个线程获得不同的instance实例(分配的内存地址不同),
  3. 这就有违单例模式了,所以这是有问题的。
  4. */
  5. public class SingletonLazy {
  6. private static SingletonLazy instance=null;
  7. private static SingletonLazy getInstance(){
  8. if(instance==null){
  9. instance=new SingletonLazy();
  10. }
  11. return instance;
  12. }
  13. }

改进1(synchronized加锁)

  1. /*
  2. 使用synchronized锁来保护临界区共享资源
  3. */
  4. public class SingletonLazy {
  5. private static SingletonLazy instance=null;
  6. private static SingletonLazy getInstance(){
  7. synchronized (SingletonLazy.class) {
  8. /*这里是线程安全的,因为每次进来都需要加锁。
  9. 打个比方,ab线程同时进来,a线程进入monitorEnter的逻辑,b线程进入entryList阻塞队列去等待锁释放,
  10. a线程初始化完instance后,进入monitorExit逻辑,释放锁。b线程还需要再次判断instance是否为null。
  11. */
  12. if(instance==null) {
  13. instance = new SingletonLazy();
  14. }
  15. }
  16. //缺点就是,不管instance是否初始化完成,后续的每个线程进来获取instance实例都需要加锁,效率太低。
  17. return instance;
  18. }
  19. }

改进2(双重检查锁)

  1. /*
  2. 双重检查锁
  3. */
  4. public class SingletonLazy {
  5. public SingletonLazy() {
  6. }
  7. private static SingletonLazy instance=null;
  8. private static SingletonLazy getInstance(){
  9. /*
  10. 这样看似已经做到线程安全了,并且后续线程进来只需判断instance是否为null,发现不为null直接返回即可。
  11. 但还是存在问题的实际上。
  12. */
  13. /*
  14. 分析:第一重检查的instance==null的逻辑判断是没有加锁的,并且也没有使用volatile关键字来修饰。所以极端情况:
  15. a、b两个线程同时进入第一重检查,都判断instance==null,而a线程加锁成功;b阻塞等待。
  16. a线程进入第二重检查发现instance==null,所以会执行instance==new SingletonLazy()的逻辑。
  17. 这个语句有两个逻辑,(1)内存分配;(2)执行init(),就是对象的构造方法;(3)静态变量的指针指向堆中的对象
  18. 所以这种情况可能会导致指令重排序,将堆中的对象的地址先赋值给静态变量,再调用init();
  19. 如果此时有c线程进来,拿到的就是一个不完整的instance实例对象。
  20. */
  21. if(instance==null) {
  22. synchronized (SingletonLazy.class) {
  23. if (instance == null) {
  24. instance = new SingletonLazy();
  25. }
  26. }
  27. }
  28. return instance;
  29. }
  30. }

改进3(volatile关键字禁止指令重排序)

  1. /*
  2. 双重检查锁+volatile关键字
  3. */
  4. public class SingletonLazy {
  5. public SingletonLazy() {
  6. }
  7. private static volatile SingletonLazy instance=null;
  8. private static SingletonLazy getInstance(){
  9. /*
  10. volatile关键字能保证共享变量在多线程之间的可见性与有序性。原理是内存屏障:
  11. storeStore屏障保证其之前的所有的读写指令执行完,不与volatile域写重排序;
  12. storeLoad屏障保证volatile域写后将高速缓存中的共享变量强制刷新到内存中,不能等到高速缓存满再刷脏。
  13. volatile域读会在前后施加loadLoad和loadStore两个屏障,
  14. loadLoad屏障保证volatile域读共享变量一定到内存中读取,让自己高速缓存的共享变量值无效;
  15. loadStore屏障保证volatile域读后下面所有的读写操作不与它重排序
  16. */
  17. if(instance==null) {
  18. synchronized (SingletonLazy.class) {
  19. if (instance == null) {
  20. instance = new SingletonLazy();
  21. }
  22. }
  23. }
  24. return instance;
  25. }
  26. }

奇偶线程交替打印1-100

方法一:ReentrantLock

  1. /*
  2. odd线程打印奇数、even线程打印偶数
  3. 方法一:使用锁机制
  4. */
  5. import java.util.concurrent.locks.Condition;
  6. import java.util.concurrent.locks.ReentrantLock;
  7. public class oneLock {
  8. private static ReentrantLock lock=new ReentrantLock();
  9. private static int count=1;
  10. private static Condition oddCondition;
  11. private static Condition evenCondition;
  12. public static void main(String[] args) throws InterruptedException {
  13. oddCondition=lock.newCondition();
  14. evenCondition=lock.newCondition();
  15. new Thread(new Runnable() {
  16. @Override
  17. public void run() {
  18. while (count<100){
  19. lock.lock();
  20. System.out.println(Thread.currentThread().getName()+"打印了"+count++);
  21. evenCondition.signalAll();
  22. try {
  23. oddCondition.await();
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. } finally {
  27. lock.unlock();
  28. }
  29. }
  30. //这儿退出while循环后需要让奇线程获取锁,让偶线程别wait了,要不然程序一直无法退出,
  31. //奇线程先拿到锁后再唤醒偶线程,最后释放锁
  32. if(count>100) {
  33. lock.lock();
  34. evenCondition.signalAll();
  35. lock.unlock();
  36. }
  37. }
  38. },"奇线程").start();
  39. //main线程在调用.start()启动后,先睡10毫秒,让奇线程一定先于偶线程执行;
  40. Thread.sleep(10);
  41. new Thread(new Runnable() {
  42. @Override
  43. public void run() {
  44. while (count<=100){
  45. lock.lock();
  46. System.out.println(Thread.currentThread().getName()+"打印了"+count++);
  47. oddCondition.signalAll();
  48. try {
  49. evenCondition.await();
  50. } catch (Exception e) {
  51. e.printStackTrace();
  52. } finally {
  53. lock.unlock();
  54. }
  55. }
  56. }
  57. },"偶线程").start();
  58. }
  59. }

方法二:volatile关键字修饰flag标志位+原子整数(cas)

  1. //cpu空转就完事了啊
  2. import java.util.concurrent.atomic.AtomicInteger;
  3. public class twoVolatile {
  4. private static volatile boolean flag=true;
  5. private static AtomicInteger count=new AtomicInteger(1);
  6. public static void main(String[] args) {
  7. new Thread(new Runnable() {
  8. @Override
  9. public void run() {
  10. while (count.get()<100){
  11. if(flag){
  12. System.out.println(Thread.currentThread().getName()+"打印了"+count.getAndIncrement());
  13. flag=false;
  14. }
  15. }
  16. }
  17. },"奇线程").start();
  18. new Thread(new Runnable() {
  19. @Override
  20. public void run() {
  21. while (count.get()<=100){
  22. if(!flag){
  23. System.out.println(Thread.currentThread().getName()+"打印了"+count.getAndIncrement());
  24. flag=true;
  25. }
  26. }
  27. }
  28. },"偶线程").start();
  29. }
  30. }

方法三:信号量Semaphore

  1. /*
  2. odd线程打印奇数、even线程打印偶数
  3. 方法三:使用信号量
  4. 因为最后一个打印的数是偶数,所以奇线程阻塞等待的时候acquire(),
  5. 因为他在99的时候判断了是小于等于100的,然后再++成100,然后再进入循环发现还是小于等于100.
  6. 所以才能进入acuire()中,所以如果奇线程的方法执行的while(count<=100)它会打印101。
  7. */
  8. public class threeSemaphore {
  9. private static Semaphore oddSemaphore=new Semaphore(1);
  10. private static Semaphore evenSemaphore=new Semaphore(0);
  11. private static int count=1;
  12. public static void main(String[] args) {
  13. new Thread(new Runnable() {
  14. @Override
  15. public void run() {
  16. while (count<100) {
  17. try {
  18. oddSemaphore.acquire();
  19. System.out.println(Thread.currentThread().getName()+"打印了"+count++);
  20. evenSemaphore.release();
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  26. }, "奇线程").start();
  27. new Thread(new Runnable() {
  28. @Override
  29. public void run() {
  30. while (count<=100) {
  31. try {
  32. evenSemaphore.acquire();
  33. System.out.println(Thread.currentThread().getName()+"打印了"+count++);
  34. oddSemaphore.release();
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }
  40. }, "偶线程").start();
  41. }
  42. }

三个窗口卖100张票(防止超卖)

方法一:加锁

  1. import java.util.concurrent.locks.ReentrantLock;
  2. /*
  3. 这里三个线程的代码都是一样的,或者直接用Runnable来创建任务即可,然后创建三个线程一起执行这个任务。
  4. 每个线程睡上10毫秒,是为了防止一个窗口把票卖完
  5. */
  6. public class oneLock {
  7. private static int count=100;
  8. private static ReentrantLock lock=new ReentrantLock();
  9. public static void main(String[] args) {
  10. new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. while (count>=1) {
  14. try {
  15. Thread.sleep(10);
  16. lock.lock();
  17. if (count >= 1) {
  18. System.out.println(Thread.currentThread().getName() + "售出了第" + count-- + "张票");
  19. } else {
  20. break;
  21. }
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. } finally {
  25. lock.unlock();
  26. }
  27. }
  28. }
  29. },"窗口二").start();
  30. new Thread(new Runnable() {
  31. @Override
  32. public void run() {
  33. while (count>=1) {
  34. try {
  35. Thread.sleep(10);
  36. lock.lock();
  37. if (count >= 1) {
  38. System.out.println(Thread.currentThread().getName() + "售出了第" + count-- + "张票");
  39. } else {
  40. break;
  41. }
  42. } catch (Exception e) {
  43. e.printStackTrace();
  44. } finally {
  45. lock.unlock();
  46. }
  47. }
  48. }
  49. },"窗口三").start();
  50. new Thread(new Runnable() {
  51. @Override
  52. public void run() {
  53. while (count>=1) {
  54. try {
  55. Thread.sleep(10);
  56. lock.lock();
  57. if (count >= 1) {
  58. System.out.println(Thread.currentThread().getName() + "售出了第" + count-- + "张票");
  59. } else {
  60. break;
  61. }
  62. } catch (Exception e) {
  63. e.printStackTrace();
  64. } finally {
  65. lock.unlock();
  66. }
  67. }
  68. }
  69. },"窗口一").start();
  70. }
  71. }

方法二:原子整数

  1. import java.util.concurrent.atomic.AtomicInteger;
  2. public class caseTwo {
  3. private static AtomicInteger count=new AtomicInteger(100);
  4. public static void main(String[] args) {
  5. new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. while (count.get()>=1){
  9. try {
  10. Thread.sleep(10);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println(Thread.currentThread().getName()+"售出了"+count.getAndDecrement()+"张票");
  15. }
  16. }
  17. }, "窗口一").start();
  18. new Thread(new Runnable() {
  19. @Override
  20. public void run() {
  21. while (count.get()>=1){
  22. try {
  23. Thread.sleep(10);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println(Thread.currentThread().getName()+"售出了"+count.getAndDecrement()+"张票");
  28. }
  29. }
  30. }, "窗口二").start();
  31. new Thread(new Runnable() {
  32. @Override
  33. public void run() {
  34. while (count.get()>=1){
  35. try {
  36. Thread.sleep(10);
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. System.out.println(Thread.currentThread().getName()+"售出了"+count.getAndDecrement()+"张票");
  41. }
  42. }
  43. }, "窗口三").start();
  44. }
  45. }