Volatile和锁Synchronized,cas(Atomic),ReentrantLock,ReadWriteLock 笔记

it2024-07-23  41

Volatile

         特点: 1 线程可见性 2 禁止指令重排序 3但是不保证原子性操作

Synchronized 锁

特性:

原子性 ,可见性,可重入锁,异常会释放锁,非公平锁

使用:

1 加在方法上,如果是非静态方法就是锁住了这个对象,这个对象多个Synchronized方法只能执行一个

2 加在方法上,如果是静态方法就是锁住了这个类,这个类多个Synchronized方法只能执行一个

3 加在对象上,Synchronized 对象同样的代码段只能执行一个

4 加在class上,Synchronized 同样的类代码段只能执行一个

锁升级:

偏向锁-》自旋锁-》重量锁

偏向锁:一个线程的时候记录这个线程,下次默认锁就是他的

自旋锁:多于一个线程,就会转换成自旋锁,默认自旋10次进入队列

重量锁:进入cpu队列不耗费cpu资源

自旋锁 什么时候用?执行时间短,线程少

系统锁  什么时候用?执行时间长,线程多

其他:

Synchronized 锁优化:

1 锁细化锁的代码越少越好

2 锁粗化,合并多个小锁

不能当为锁的对象:

String,Integer,Character、Short、Long因为有缓存,会导致对象改变问题(基础类型int啥的也不行)

死锁的例子:

https://www.cnblogs.com/pweizhao/articles/9066728.html

 

Cas(无锁优化 自旋)compare and set  比较 设定

AtomicInteger 线程安全的integer 好多Atomic开口的类都是线程安全的

用 increamnet添加数

这些类保证线程安全用的是Cas(要改的值V,期望值Expected,改后的值NewValue) CPU指令级别中间不会被打断

Unsafe 类 直接操作java虚拟机内存

基于CAS实现的类java.util.concurrent.atomic

ABA问题

1 如果有3个线程   分别是 +1 -1 +1的操作 从结果来说是没区别的

2 但是对象类型的就不行,是有区别的,要加版本号解决这个问题

LongAdder,AtomicLong,Synchronized 三个关于累加的对比

例子

static long count2 = 0L; static AtomicLong count1 = new AtomicLong(0L); static LongAdder count3 = new LongAdder(); public static void main(String[] args) throws Exception { Thread[] threads = new Thread[1000]; for(int i=0; i<threads.length; i++) { threads[i] = new Thread(()-> { for(int k=0; k<100000; k++) count1.incrementAndGet(); }); } long start = System.currentTimeMillis(); for(Thread t : threads ) t.start(); for (Thread t : threads) t.join(); long end = System.currentTimeMillis(); //TimeUnit.SECONDS.sleep(10); System.out.println("Atomic: " + count1.get() + " time " + (end-start)); //----------------------------------------------------------- Object lock = new Object(); for(int i=0; i<threads.length; i++) { threads[i] = new Thread(new Runnable() { @Override public void run() { for (int k = 0; k < 100000; k++) synchronized (lock) { count2++; } } }); } start = System.currentTimeMillis(); for(Thread t : threads ) t.start(); for (Thread t : threads) t.join(); end = System.currentTimeMillis(); System.out.println("Sync: " + count2 + " time " + (end-start)); //---------------------------------- for(int i=0; i<threads.length; i++) { threads[i] = new Thread(()-> { for(int k=0; k<100000; k++) count3.increment(); }); } start = System.currentTimeMillis(); for(Thread t : threads ) t.start(); for (Thread t : threads) t.join(); end = System.currentTimeMillis(); //TimeUnit.SECONDS.sleep(10); System.out.println("LongAdder: " + count1.longValue() + " time " + (end-start)); }

输出

Atomic: 100000000 time 1523 Sync: 100000000 time 3819 LongAdder: 100000000 time 454

结论

Atomic   用的cas所以快于Sync

LongAdder 用的分段锁+cas ,线程越多比Atomic越快,线程少Atomic快

Lock锁的实现

1 ReentrantLock

使用  Lock lock= new ReentrantLock   加锁Lock.lock 解锁Lock.unlock 

比snchronized的好处

1 trylock 可以申请几秒之内得到一把锁

2 lockinterruptibly 

3 公平锁排队取锁

例子:

//private static ReentrantLock lock=new ReentrantLock(true); //参数为true表示为公平锁,请对比输出结果 public static void main(String[] args) { Lock lock = new ReentrantLock(); Thread t1 = new Thread(()->{ try { lock.lock(); System.out.println("t1 start"); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); System.out.println("t1 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { lock.unlock(); } }); t1.start(); Thread t2 = new Thread(()->{ try { //lock.lock(); lock.lockInterruptibly(); //可以对interrupt()方法做出响应 System.out.println("t2 start"); TimeUnit.SECONDS.sleep(5); System.out.println("t2 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { lock.unlock(); } }); t2.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t2.interrupt(); //打断线程2的等待 }

2 ReadWriteLock

读锁  可以有多个读锁与写锁互斥

写锁 只能有一个写锁

例子:

static Lock lock = new ReentrantLock(); private static int value; static ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); static Lock readLock = readWriteLock.readLock(); static Lock writeLock = readWriteLock.writeLock(); public static void read(Lock lock) { try { lock.lock(); Thread.sleep(1000); System.out.println("read over!"); //模拟读取操作 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void write(Lock lock, int v) { try { lock.lock(); Thread.sleep(1000); value = v; System.out.println("write over!"); //模拟写操作 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main(String[] args) { //Runnable readR = ()-> read(lock); Runnable readR = ()-> read(readLock); //Runnable writeR = ()->write(lock, new Random().nextInt()); Runnable writeR = ()->write(writeLock, new Random().nextInt()); for(int i=0; i<18; i++) new Thread(readR).start(); for(int i=0; i<2; i++) new Thread(writeR).start(); }
最新回复(0)