java并发系列之synchronized与lock

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锁适合代码少量的同步问题
发布了89 篇原创文章 · 获赞 67 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/xl_1803/article/details/100061957