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();
}