线程其实就是程序的执行单元,负责自己的运行模块,就比如王者荣耀里面的小兵都是一个个的线程。
懒汉式单例,代码如下:

这是个最基本的懒汉式单例,现在假设开启两个线程t1,t2。 t1线程执行完了第10行代码 ,恰好给t1线程的时间片用完,没有cpu的执行权了,但是还没有来得及执行第11行代码。
此时同时t2线程也过来执行第10、11行代码,当执行完了第11行代码时,假设刚t2的时间片用完了,释放执行权,t1线程争夺到了执行权,又会开始执行第11代码,又new了一个新对象,分析到这里我们的单例设计模式已经出现问题了。产生了多个对象。
那么怎么解决多线程带来的安全问题?
这种由多线程带来的线程安全问题,大家第一时间都会想到用同步锁来解决线程之间对于共享资源的竞争问题,常用的两种锁如下:
这个锁可以用于同步代码块,方法上,但是要注意是锁的对象是谁。代码改进如下所示:

但是在这个方法上面加锁的话,我们没有办法对其进行性能优化,所以我们可以改成同步代码块的方式,对其进行性能优化,代码如下所示:

改成了同步代码块,我们分析下,t1,t2线程过来都要加锁进行判断,t1加锁成功,t2去尝试加锁其实实在做无用功,浪费cpu的资源,那么怎么优化呢?我们可以在外面再加一层判断,代码如下所示:

这样我们就可以提高懒汉式单例的性能。
使用Lock锁住第15,16行代码,保证两行代码的原子性即可。

这里我们模拟3个窗口(其实是3个线程)一起卖10张车票的场景。
10张车票代码如下:

我们这里每执行一次就减1,代表每卖出一张票就会少一张票。
然后模拟窗口代码如下:

卖票的任务就交给多线程去跑,所以这里需要实现Runnable接口,这里我们为了看效果,所以使用while(true)做测试。
然后开启三个窗口代码如下:

然后给三个线程起一个别名方便看效果。
运行结果如下所示:

从运行结果分析出,非常不符合现实的问题,就是窗口1卖了0车票,窗口3卖了-1车票,很离谱,不符合现实,不可能出现0、-1这种车票。从这里我们就引发出来线程安全问题。这里可以使用Lock和synchronized锁。
分析一下问题,我们回头看下车票资源,是线程之间共享的资源,代码如下:

我们假设没有加锁之前,我们先假设count=1
然后t1线程执行完第11行代码,然后暂停,此时是没有执行count--操作的哦,此时count=1
然后t2线程过来了,也执行完了第11行代码,然后暂停,也没有执行count--,此时count=1
然后t3过来同样的,也执行完了第11行代码,然后暂停,也没有执行count--,此时count=1
现在三个线程都已经执行完了这个第11行的判断条件,都进来了,停在了第13行代码
假设t1醒来,执行完了15行代码,输出结果为1,此时因为执行了count--操作,所以内存中count=0
然后t2醒来,此时主内存中的count等于0,t2输出结果为 0,同时执行count--操作,则count=-1
最后t3醒来,此时主内存中的count等于0,执行完了15行代码,t3输出结果为-1,内存中count=-2
最终分析出问题就是出在了第11行代码和第16行代码没有保证原子性,我们可以将sale()方法加同步锁synchronized,如下所示:

这样就能保证正常卖票了。
同时我们也可以使用Lock进行加锁代码如下所示:

最终运行结果如下所示:
