zoukankan      html  css  js  c++  java
  • Java高并发编程(二)

       一、高并发编程的几个部分

      synchronized同步器、jdk提供的同步容器、ThreadPool线程池、executor执行器

      二、重入锁

      1.reentrantlock关键字(相较于synchronized更加灵活)

      2.reentrantlock用于替代synchronized,在使用此锁时必须要手动释放锁,在使用synchronized锁时遇到异常时,jvm会自动释放锁,但是reentrantlock必须要手动释放锁,因此经常在finally中进行锁的释放

      Lock  lock = new Reentrantlock();//将参数设置为true时此锁为公平锁

      lock.lock();//相当于synchronized(this)

      lock.unlock();//开锁

      3.使用reentrantlock可以进行尝试锁定“tryLock”,这样在指定时间内无法锁定,线程可决定是否继续等待

      Boolean locked =lock.tryLock();

      if(locked)lock.unlock();

      先假定没有得到锁

      Boolean locked = false;

      try{

        locked = lock.tryLock(5,TimeUnit.SECONDS);//先尝试等5秒

        //代码逻辑,根据返回值判断

      }catch{

      }finally{

        if(locked)

          lock.unlock();//根据返回值来判断是否开锁,如果已经得到锁就打开,未得到就不开

      }

      4.使用reentrantlock可以调用lockInterruptibly方法,可以相应interrupt方法作出响应(可被打断方法),使用其他线程打断当前线程等待

      5.reentrantlock还可以被指定为公平锁,而synchronized为非公平锁(非公平锁的效率相对公平锁来说较高)

        公平锁:可以假定谁等待时间长谁获得锁

        非公平锁:不考虑等待时间

      三、经典面试题

      生产者-消费者模式:写一个固定容量的同步容器,拥有put和get方法,以及getCount方法,能够支持两个生产者线程以及是个消费者线程的阻塞调用

      同步容器:多个线程访问不会出问题,容器满了的话,调用put的线程需要等待,容器空了的话调用get的线程需要等待

      1.使用wait和notify/notifyAll来实现,wait99.9%的情况下与while一起使用,if是只判断一次,while是每次要运行之前都进行判断,不加notify的话可能会产生死锁,假定当前等待线程为生产者线程,此时notify又唤醒一个生产者进程就造成了死锁

    public class MyContainer1<T> {
    	final private LinkedList<T> lists = new LinkedList<>();
    	final private int MAX = 10; //最多10个元素
    	private int count = 0;
    	
    	
    	public synchronized void put(T t) {
    		while(lists.size() == MAX) { //想想为什么用while而不是用if?
    			try {
    				this.wait(); //effective java
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		
    		lists.add(t);
    		++count;
    		this.notifyAll(); //通知消费者线程进行消费
    	}
    	
    	public synchronized T get() {
    		T t = null;
    		while(lists.size() == 0) {
    			try {
    				this.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		t = lists.removeFirst();
    		count --;
    		this.notifyAll(); //通知生产者进行生产
    		return t;
    	}
    	
    	public static void main(String[] args) {
    		MyContainer1<String> c = new MyContainer1<>();
    		//启动消费者线程
    		for(int i=0; i<10; i++) {
    			new Thread(()->{
    				for(int j=0; j<5; j++) System.out.println(c.get());
    			}, "c" + i).start();
    		}
    		
    		try {
    			TimeUnit.SECONDS.sleep(2);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		//启动生产者线程
    		for(int i=0; i<2; i++) {
    			new Thread(()->{
    				for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j);
    			}, "p" + i).start();
    		}
    	}
    }

      2.使用lock和Condition来实现,对比两种方式,Condition的方式可以更加精确的指定哪些线程被唤醒

    public class MyContainer2<T> {
    	final private LinkedList<T> lists = new LinkedList<>();
    	final private int MAX = 10; //最多10个元素
    	private int count = 0;
    	
    	private Lock lock = new ReentrantLock();
    	private Condition producer = lock.newCondition();
    	private Condition consumer = lock.newCondition();
    	
    	public void put(T t) {
    		try {
    			lock.lock();
    			while(lists.size() == MAX) { //想想为什么用while而不是用if?
    				producer.await();
    			}
    			
    			lists.add(t);
    			++count;
    			consumer.signalAll(); //通知消费者线程进行消费
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    	}
    	
    	public T get() {
    		T t = null;
    		try {
    			lock.lock();
    			while(lists.size() == 0) {
    				consumer.await();
    			}
    			t = lists.removeFirst();
    			count --;
    			producer.signalAll(); //通知生产者进行生产
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} finally {
    			lock.unlock();
    		}
    		return t;
    	}
    	
    	public static void main(String[] args) {
    		MyContainer2<String> c = new MyContainer2<>();
    		//启动消费者线程
    		for(int i=0; i<10; i++) {
    			new Thread(()->{
    				for(int j=0; j<5; j++) System.out.println(c.get());
    			}, "c" + i).start();
    		}
    		
    		try {
    			TimeUnit.SECONDS.sleep(2);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		//启动生产者线程
    		for(int i=0; i<2; i++) {
    			new Thread(()->{
    				for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j);
    			}, "p" + i).start();
    		}
    	}
    }
    

      

      四、线程局部变量

      1.ThreadLocal关键字,线程局部变量

      2.线程局部变量之间互不影响,框架中大量使用,ThreadLocal使用空间换时间,而synchronized是使用时间换空间

    public class ThreadLocal1 {
    	/*volatile*/ static Person p = new Person();
    	
    	public static void main(String[] args) {
    				
    		new Thread(()->{
    			try {
    				TimeUnit.SECONDS.sleep(2);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			
    			System.out.println(p.name);
    		}).start();
    		
    		new Thread(()->{
    			try {
    				TimeUnit.SECONDS.sleep(1);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			p.name = "lisi";
    		}).start();
    	}
    }
    
    public class ThreadLocal2 {
    	//volatile static Person p = new Person();
    	static ThreadLocal<Person> tl = new ThreadLocal<>();
    	
    	public static void main(String[] args) {
    				
    		new Thread(()->{
    			try {
    				TimeUnit.SECONDS.sleep(2);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			
    			System.out.println(tl.get());
    		}).start();
    		
    		new Thread(()->{
    			try {
    				TimeUnit.SECONDS.sleep(1);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			tl.set(new Person());
    		}).start(); 
    	}
    	
    	static class Person {
    		String name = "zhangsan";
    	}
    }
    

      补充:

      整理出来的高并发编程比较基础得知识点与面试题,如有不足也请指正,总之希望对大家有帮助。

  • 相关阅读:
    Java菜鸟学习笔记(3)--Java API 文档下载与文档注释的使用
    常用控制台命令大全-Ubuntu篇
    Java菜鸟学习笔记(2)--Ubuntu JDK环境变量配置与常见问题
    Java菜鸟学习笔记(1)--Windows JDK环境变量配置与常见问题
    Java菜鸟学习笔记(x)-《java学习圣经宝典》笔记
    自我管理&时间管理(转)
    方滨兴校长在2013届本科生毕业典礼上的讲话(转)
    static Map的使用
    好的测试网站
    经典css网站
  • 原文地址:https://www.cnblogs.com/ghoster/p/7478803.html
Copyright © 2011-2022 走看看