• 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() + " ";
    	}
    }
    
    
  • 相关阅读:
    Codeforce-Power Tower(欧拉降幂)
    Caesar Cipher
    BZOJ-1143-祭祀river(二分图-偏序集最大反链)
    商务英语中级第三版 MODULE2 Task 3: Listen to the presentation and write what each refers to.
    计算机网络第一章学习笔记
    第一篇博客随笔
    子页面传递数组给父页面
    第6课第4节_Binder系统_驱动情景分析_服务注册过程_分析
    opencv Mat 与MFC中的CImage相互转换
    多文档中建立一个对话框类,通过这个方法来在其他类中得到对话框对象指针,访问对话框成员
  • 原文地址:https://www.cnblogs.com/yinchh/p/12395100.html
Copyright © 2020-2023  润新知