1, 单例模式
package y.i.d;
/**
* DCL就是doubel checked lock 双重检测锁
* 这个demo 是单例的懒汉模式 (饿汉就是直接new好了对象,懒汉就是什么时候什么时候new)
* 主要利用 synchronized volatile 和 dcl 来实现;
* 第一步私有化 构造方法 是外部不能拿到
* 第二步 static 私有化变量 存储 实例
* 第三步 static 写获取 实例的方法
* 然后在这个基础上再来使得最终只有一个实例生成
* */
public class DCLTest {
private DCLTest() { //第一步私有化 构造方法 是外部不能拿到
}
// volatile 使代码最终完善,不会出现 多线程拿到空对象的情况
private volatile static DCLTest instance; // 第二步 私有化变量 存储 实例
public static DCLTest getInstance() {
if(null!=instance) { // dcl 提高效率
return instance;
}
synchronized (DCLTest.class) { // 此处同步多线程 ,锁住模子;
if(null==instance) {
instance = new DCLTest();// 假如new 要花很长时间 这个时候就会出现 第二个取得了空值的情况
}
}
return instance;
}
public static void main(String[] args){
Thread t1=new Thread(()-> {
System.out.println(DCLTest.getInstance());
});
t1.start();
System.out.println(DCLTest.getInstance());
}
}
2,ThreadLocal
package y.i.d;
/**
* ThreadLocal 是各个线程的本地存储, 独立不干扰
* 一般使用 private static 修饰
*
* */
public class ThreadLocalTest {
// 不设置初始值
// private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
// 设置初始值1
// private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
// protected Integer initialValue() {
// return 200;
// }
// };
// 设置初始值2
private static ThreadLocal<Integer> threadLocal =ThreadLocal.withInitial(()->200);
public static void main(String[] args){
threadLocal.set(1000);
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal.get()); //1000
new Thread(new MyRun(),"xiaoming").start();
}
static class MyRun implements Runnable{
@Override
public void run() {
threadLocal.set(999);
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal.get());// 999
}
}
}
ThreadLocal 的上下文
package y.i.d;
/**
* ThreadLocal 是各个线程的本地存储, 独立不干扰
* 一般使用 private static 修饰
*
* */
public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal =ThreadLocal.withInitial(()->200);
public static void main(String[] args){
threadLocal.set(1000);
new Thread(new MyRun(),"xiaoming").start(); // 线程起点
}
static class MyRun implements Runnable{
public MyRun() {
// 此处时由 main 起的 所以上下文依然是main的
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal.get());// 1000
}
@Override
public void run() {
threadLocal.set(999);
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal.get());// 999
}
}
}
ThreadLocal的子类 InheritableThreadLocal 实现上下文继承,会从起点 拷贝一份数据给子线程
package y.i.d;
/**
* ThreadLocal 是各个线程的本地存储, 独立不干扰
* 一般使用 private static 修饰
*
* */
public class ThreadLocalTest {
// 子线程不会继承 起点 值null
// private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
// 子线程会继承 起点 值1000
private static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<Integer>();
public static void main(String[] args){
threadLocal.set(1000);
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal.get());
new Thread(new MyRun(),"xiaoming").start();
}
static class MyRun implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"+threadLocal.get());
}
}
}
3, 可重入锁 ReentrantLock
锁大部分内置都是可重入的,如果一个线程 想要获取他已经持有的锁的时候 那么他会立即成功,同时 内部有一个计数器,count++, count ==0 锁释放。 如果没有这种机制,那么就发生死锁。
可重入锁 机制其实就是一个类 下面实现一下
不可重入锁
package y.i.d;
public class ReentrantLockTest {
Lock lock ;
public ReentrantLockTest() {
this.lock= new Lock();
}
public void a() throws InterruptedException {
lock.lock(); // 占用了资源
dst();
lock.unLock();
}
public void dst() throws InterruptedException {
lock.lock(); // 死锁
// ...
lock.unLock();
}
public static void main(String[] args) throws InterruptedException{
ReentrantLockTest r = new ReentrantLockTest();
r.a();
}
}
class Lock{
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException {
while(isLocked) {
this.wait();
}
isLocked = true;
}
public synchronized void unLock() {
isLocked = false;
notify();
}
}
可重入锁
package y.i.d;
public class ReentrantLockTest {
Lock lock ;
public ReentrantLockTest() {
this.lock= new Lock();
}
public void a() throws InterruptedException {
lock.lock();
System.out.println(lock.getCount());
dst();
lock.unLock();
System.out.println(lock.getCount());
}
public void dst() throws InterruptedException {
lock.lock();
System.out.println(lock.getCount());
// ...
lock.unLock();
System.out.println(lock.getCount());
}
public static void main(String[] args) throws InterruptedException{
ReentrantLockTest r = new ReentrantLockTest();
r.a();
}
}
class Lock{
private boolean isLocked = false;
private int count = 0;
private Thread thread = null;
public synchronized void lock() throws InterruptedException {
Thread t1 = Thread.currentThread();
while(isLocked && thread!=t1) {
this.wait();
}
count++;
isLocked = true;
thread = t1;
}
public synchronized void unLock() {
if(thread ==Thread.currentThread()) {
count--;
if(count==0) {
isLocked = false;
notify();
thread=null;
}
}
}
public int getCount() {
return count;
}
}
4,乐观锁 CAS
锁的分类有很多种
按照可沿用性 可以分为 可重入锁 和 非可重入锁;
按照公平性,可以分为公平锁 和 不公平锁;
按照加锁不加锁 可以分为 悲观锁和乐观锁;
悲观锁加锁,然后等锁解除才有后续操作;
乐观锁 就是不加锁,每次都去重新尝试 操作,失败了就重试知道成功位置;
乐观锁一般使用CAS来实现,Compare and swap 比较并且交换 ;
就是要更新一个值v, 比较 原来的值a,如果原来的值a对上了就更新 这个值v;
但是会存在ABA的问题;所以需要加版本控制一下;
就比较 版本 和 值 ,如果版本对上了,更新值并且 更新版本,
CAS属于原子操作;不会被外部打断;实现属于cpu 级别的硬件实现;效率很高
package y.i.d;
import java.util.concurrent.atomic.AtomicInteger;
public class CAS {
// Atomic * 就是CAS来实现的
private static AtomicInteger stock = new AtomicInteger(5);
public static void main(String[] args){
for (int i = 0; i < 6; i++) {
new Thread(()->{
// stock.decrementAndGet() 是CAS操作 减法 -1
Integer left = stock.decrementAndGet();
if(left<0) { // 有歧义
System.out.println(Thread.currentThread().getName()+"没抢到"+left);
return;
}
System.out.println(Thread.currentThread().getName()+"-->"+"抢到一件商品"+"还剩"+left+"件商品");
}) .start();
}
}
}