java中的锁

悲观锁和乐观锁

  1. 悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改;Java中,synchronized关键字和Lock的实现类都是悲观锁
  2. 乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据,则报错或者重试;Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的
  3. 适用场景:悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确;乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升;
悲观锁1
	public synchronized void add() {
            //同步操作
	}
悲观锁2
	private ReentrantLock lock = new ReentrantLock();
	public void add() {
		lock.lock();
		//同步操作
		lock.unlock();
	}
乐观锁
	private AtomicInteger atomicInteger = new AtomicInteger();
	public void add() {
		atomicInteger.incrementAndGet();// 执行自增1
	}

其他注意点

  • 实例方法使用synchronized锁定的是当前实例
  • 静态方法使用synchronized锁定的是当前类的所有实例
  • synchronized(obj)同步代码块使用obj对象的监视器实现同步

synchronized关键字和Lock对象的选择

  • 最好两个都不用,使用一种java.util.concurrent包提供的机制
  • 如果synchronized关键字能满足用户的需求,就用synchronized

volatile

  • 线程间可见
    多线程情况下,内存和寄存器中的值不一定一样,每次线程要访问volatile修饰的变量时都是从内存中读取,而不是存寄存器中读取,因此每个线程访问到的变量值都是一样的
  • 禁止指令重排序
    单例模式中double check lock一定要配合volatile使用

ThreadLocal

使用ThreadLocal管理变量,则多线程情况下,每一个线程拥有一个该变量的副本,更改后不影响其他线程;简而言之,ThreadLocal用空间获取了时间,而同步锁用时间换取了空间

wait、notify

  • Object类有两个监视器相关的方法wait、notify
  • synchronized和wait、notify同时使用,wait、notify必须在synchronized中
/*
 * 使用wait、notifyAll实现生产者消费者
 */
public class ProducerConsumer {

	public static void main(String[] args) {
		ProCon proCon = new ProCon();
		
		//生产者
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for (int i = 0; i < 20; i++) {
						proCon.pro(i);
						Thread.sleep(1000);
					}
				} catch (InterruptedException e) {
				}
			}
		}).start();
		
		//消费者1
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while (true) {
						proCon.con();
						Thread.sleep(3000);
					}
				} catch (InterruptedException e) {
				}
			}
		}).start();

		//消费者2
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while (true) {
						proCon.con();
						Thread.sleep(3000);
					}
				} catch (InterruptedException e) {
				}
			}
		}).start();
	}

}

class ProCon {
	List<Integer> listProduct = new ArrayList<Integer>();

	public synchronized void pro(int pro) throws InterruptedException {
		if (listProduct.size() > 100) {
			System.out.println(getPrefix() + "库存已满,请稍候再生产");
			wait();
		}

		listProduct.add(pro);
		System.out.println(getPrefix() + "生产了:" + pro);
		notifyAll();// 通知消费者获取产品
	}

	public synchronized void con() throws InterruptedException {
		if (listProduct.size() <= 0) {
			System.out.println(getPrefix() + "库存不足,请及时补货");
			wait();
		}

		Integer cur = listProduct.get(0);
		listProduct.remove(cur);
		System.out.println(getPrefix() + "取走:" + cur + " 剩余库存:" + listProduct.toString());
		notifyAll();// 通知生产者生产产品
	}

	private String getPrefix() {
		return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()) + Thread.currentThread().getName() + " ";
	}
}

posted @ 2020-03-02 13:10  惬意的小屋  阅读(141)  评论(0编辑  收藏  举报