synchronized
package thread;
/**
* synchronized修饰实例方法、静态方法、代码块,修饰实例方法和代码块时候用this对象当锁,修改静态方法时用类的class对象当锁
* synchronized是悲观锁,synchronized和ReentrantLock等独占锁就是悲观锁思想的实现
* synchronized是可重入锁
* synchronized是非公平锁
* 作为锁的条件:必须是对象且被多个线程共享才能作为锁
*
*/
public class SynchronizedDemo {
private int a;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public void add(){
synchronized(this){
int x = getA();
setA(x+1);
}
}
public static void main(String[] args) throws InterruptedException {
final SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
for (int i = 0; i < 100000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
synchronizedDemo.add();
}
}).start();
}
Thread.sleep(10000);
System.err.println(synchronizedDemo.getA());
}
}
lock
package thread;
import java.util.concurrent.locks.ReentrantLock;
/**
* 1、锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂,因为锁(以及其它更高级的线程同步机制)
* 是由synchronized同步块的方式实现的
*
* 2、乐观锁与悲观锁
* 乐观锁适用于读比较多的场景,悲观锁适用于写比较多的场景,不加锁会带来大量的性能提升
* 乐观锁常见的两种实现方式:版本号机制或CAS算法实现
* 版本号机制:一般是在数据表中加上一个数据版本号version字段,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,
* 在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功
*
* 3、CAS算法
* 即 compare and swap(比较与交换),是一种有名的无锁算法,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,
* 所以也叫非阻塞同步
* check and act模式,先检查后操作模式 首先检查一个变量的值,然后再基于这个值做一些操作,check then act操作必须是原子的。
* 原子就是说”check“操作和”act“被当做一个原子代码块执行。不存在多个线程同时执行原子块
* CAS有3个操作数,内存值V,预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做
*
* 4、可重入锁:假如一把锁锁了n个地方,那么只要得到这把锁,那n个地方都可以访问
*
* 5、独享锁和共享锁
* 独享锁:是指该锁一次只能被一个线程所持有。实现:ReentrantLock
* 共享锁:是指该锁可被多个线程所持有。实现:ReadWriteLock,读锁是共享锁,写锁是独享锁。因为读读可以同时进行,而读写、写写不能同时进行
*
* 6、公平锁、非公平锁:公平锁是指多个线程按照申请锁的顺序来获取锁。
*
* 7、分段锁
* 分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作
* 当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,
* 只要不是放在一个分段中,就实现了真正的并行的插入
*
* 8、偏向锁/轻量级锁/重量级锁,这三种锁是指锁的状态,并且是针对Synchronized
*
* 9、AQS是抽象队列同步器,维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)
*
* 10、自旋锁:在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
* 获取不到锁的线程要么阻塞,要么自旋
*/
public class LockDemo {
private int a;
//通过构造函数指定是否为公平锁
private ReentrantLock reentrantLock=new ReentrantLock(true);
//private ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock();
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public void add(){
reentrantLock.lock();
//reentrantReadWriteLock.readLock().lock();
try {
int x = getA();
setA(x+1);
} finally {
reentrantLock.unlock();
//reentrantReadWriteLock.readLock().unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final LockDemo lockDemo = new LockDemo();
for (int i = 0; i < 100000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
lockDemo.add();
}
}).start();
}
Thread.sleep(10000);
System.err.println(lockDemo.getA());
}
}
两者区别
1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2.synchronized无法判断是否获取到锁,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(执行完代码或发生异常会释放锁),Lock需在finally中手工释放锁,否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可中断、可以设置是否公平
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题