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; /** * 一、锁像synchronized同步块同样,是一种线程同步机制,但比Java中的synchronized同步块更复杂,由于锁(以及其它更高级的线程同步机制) * 是由synchronized同步块的方式实现的 * * 二、乐观锁与悲观锁 * 乐观锁适用于读比较多的场景,悲观锁适用于写比较多的场景,不加锁会带来大量的性能提高 * 乐观锁常见的两种实现方式:版本号机制或CAS算法实现 * 版本号机制:通常是在数据表中加上一个数据版本号version字段,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值, * 在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,不然重试更新操做,直到更新成功 * * 三、CAS算法 * 即 compare and swap(比较与交换),是一种有名的无锁算法,即不使用锁的状况下实现多线程之间的变量同步,也就是在没有线程被阻塞的状况下实现变量的同步, * 因此也叫非阻塞同步 * check and act模式,先检查后操做模式 首先检查一个变量的值,而后再基于这个值作一些操做,check then act操做必须是原子的。 * 原子就是说”check“操做和”act“被当作一个原子代码块执行。不存在多个线程同时执行原子块 * CAS有3个操做数,内存值V,预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作 * * 四、可重入锁:假如一把锁锁了n个地方,那么只要获得这把锁,那n个地方均可以访问 * * 五、独享锁和共享锁 * 独享锁:是指该锁一次只能被一个线程所持有。实现:ReentrantLock * 共享锁:是指该锁可被多个线程所持有。实现:ReadWriteLock,读锁是共享锁,写锁是独享锁。由于读读能够同时进行,而读写、写写不能同时进行 * * 六、公平锁、非公平锁:公平锁是指多个线程按照申请锁的顺序来获取锁。 * * 七、分段锁 * 分段锁实际上是一种锁的设计,并非具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是经过分段锁的形式来实现高效的并发操做 * 当须要put元素的时候,并非对整个hashmap进行加锁,而是先经过hashcode来知道他要放在哪个分段中,而后对这个分段进行加锁,因此当多线程put的时候, * 只要不是放在一个分段中,就实现了真正的并行的插入 * * 八、偏向锁/轻量级锁/重量级锁,这三种锁是指锁的状态,而且是针对Synchronized * * 九、AQS是抽象队列同步器,维护了一个volatile int state(表明共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列) * * 十、自旋锁:在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锁适合代码少许的同步问题