Java学习路线之并发编程(五)

目录

一、线程和并发

      1.1 线程介绍

      1.2 线程安全,实现线程安全的主要方法

      1.3 线程的优点和缺点?

      1.4 并发级别

二、Java 内存模型

       2.1 什么是 Java 内存模型,以及Java内存模型的作用?

       2.2 Happen Before 原则

三、锁机制

       3.1 内置锁

       3.2 可重入锁

       3.3 锁优化

       3.4 锁优化

       3.5 死锁的四个条件和死锁避免

       3.6 synchronized 和 Lock 锁的区别?

       3.7 sleep() 和 wait() 方法的区别?

四、并发容器类

       4.1 同步容器类

       4.2 同步并发类

      4.3 同步工具类

      4.4 Executor

五、并发设计模式

      5.1 Future 模式

      5.2 生产者消费者模式

      5.3 单例模式

      5.4 CopyOnWrite(String)


 

 

一、线程和并发

      1.1 线程介绍

              1)任务调度和分配的基本单位,一个进程内有多个线程,共享进程中的资源,减少进程切换的时空开销,提升并发效率。

              2)线程的五个状态:NEW、RUNNABLE、BLOCKED、WAIT/TIMED_WAITING、TERMINATED。

              3)线程同步的方式:

通过 Object 的 wait() 和 notify();(用在sychronized锁中)
通过 Condition 的 awiat() 和 signal();(用在Reentrant Lock锁中)
通过阻塞队列;
通过线程池的Callback回调;
通过同步辅助类CountDownLatch;
通过同步辅助类CyclicBarrier;
信号量 Semphare;

 

      1.2 线程安全,实现线程安全的主要方法

              多个线程访问某个类,始终保持正确的行为。

              1、互斥同步(悲观锁);

              2、非互斥同步(乐观锁)。

 

      1.3 线程的优点和缺点?

         优点:1)充分利用多核 CPU 的资源,提升系统效率;

                    2)建模简单;

                    3)异步事件的简单处理。

         缺点:1)安全性问题(程序执行顺序);

                    2)死锁;

                    3)资源消耗过高、吞吐率过低、响应不灵敏等。

 

      1.4 并发级别

              1)阻塞:sychronized关键字;

              2)无饥饿:公平队列;

              3)无障碍:一致性标记;

              4)无锁:CAS;

              5)无等待:CopyOnWriteArrayList;

 

二、Java 内存模型

       2.1 什么是 Java 内存模型,以及Java内存模型的作用?

               Java 内存模型定义各个变量的访问规则,由 violate、final、sychronized 关键字实现原子性、可见性、有序性,以及 happen before 原则组成,happen before 原则:程序顺序规则、管程规则、violate规则、线程start()规则、线程join()规则、传递性规则。

 

       2.2 Happen Before 原则

              1、程序顺序规则:

              2、管程规则:

              3、violate规则:

             4、线程start()规则:

             5、线程join()规则:

             6、传递性规则:

 

三、锁机制

       3.1 内置锁:sychronized

              JVM 基于 Monitor 对象,利用 monitorenter 和 monitorexit 这两个字节码指令实现。

 

       3.2 可重入锁:ReetrantLock

              ReetrantLock 底层实现原理AbstractQueuedSynchronizer(抽象队列同步器):核心数据结构:双向链表 + state(锁状态)、底层操作:CAS。

              创建 ReentrantLock 的对象的时候 ,其实是创建了一个 NonfairSync() 对象,NonfairSync类是静态内部类,他继承了Sync,Sync继承了AbstractQueuedSynchronizer。

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

       3.3 锁

              重量级锁:Synchronized 的重量级锁是通过对象内部的一个叫做监视器锁(monitor)来实现的,监视器锁本质又是依赖于底层操作系统的Mutex Lock(互斥锁)来实现的。而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间。

              1、自旋锁:

              2、轻量级锁:

              3、所偏向:

              4、锁消除:是指虚拟机即时编译器(JIT)在运行时,对一些代码上要求同步,但是检测到不可能发生数据竞争的锁进行消除。

              5、锁粗化:

 

       3.4 锁优化

               1、缩小锁的范围;

               2、减小锁的粒度:即使用对个对象内置锁。

 

       3.5 死锁的四个条件和死锁避免

              因为申请资源而互相等待其他线程释放资源,

              死锁的四个必要条件:1)互斥;2)占有并等待;3)不剥夺;4)循环等待。

              破坏死锁的四个必要条件之一,以及银行家算法。

 

       3.6 synchronized 和 Lock 锁的区别?

              1、synchronized 适合线程竞争不激烈的时候;Lock 适合线程竞争激烈的时候。

              2、Lock 是可中断的,可设置为公平锁,申请限时等待;

              3、synchronized 是对象的内置锁;Lock 是 ReetrantLock 类可重入锁;

              4、synchronized 可用来修饰方法和代码块,托管给JVM执行;Lock 需要通过代码实现,一般在 try/catch、finally 代码块中手动编写和释放;

 

       3.7 sleep() 和 wait() 方法的区别?

              1、sleep()是Thread类的方法,用于控制自身的流程;wait() 是 Object() 类的方法用于线程同步;

              2、sleep()不释放锁;wait()释放锁;

              3、sleep()可以在任何地方;wait()只能在 sychronized 修饰的方法和代码块中。

 

四、并发容器类

       4.1 同步容器类

              1)Voctor;

              2)HashTable。

 

       4.2 同步并发类

              1) 点击蓝色字体,跳转到详细介绍界面,ConcurrentHashMap;

 

              2)CopyOnWriteArrayList;

              写时复制, 在往集合中添加数据的时候,先拷贝存储的数组,然后添加元素到拷贝好的数组中,然后用现在的数组去替换成员变量的数组(就是get等读取操作读取的数组)。多读取,少添加。

 

              3)ReentrantReadWriteLock:每次只能有一个写线程,但是可以有多个线程并发地读数据。

/**
 * 基于读写锁和 CountDown 闭锁实现读写锁和 Lock 锁的比较
 * @author TSjia
 *
 */

public class ReadWriteLockDemo {

	private static Lock lock = new ReentrantLock();
	
	private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	
	private static Lock readLock = readWriteLock.readLock();
	private static Lock writeLock = readWriteLock.writeLock();
	
	public static CountDownLatch end = new CountDownLatch(20);
	
	private int value;
	
	public Object handleRead(Lock lock) throws InterruptedException {
		
		try {
			lock.lock();
			Thread.sleep(1000);
			return value;
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			end.countDown();
			lock.unlock();
		}
		return 0;
	}
	
	public void handleWrite(Lock lock, int index) throws InterruptedException {
		
		try {
			lock.lock();
			Thread.sleep(1000);
			value = index;
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			end.countDown();
			lock.unlock();
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		
		final ReadWriteLockDemo demo = new ReadWriteLockDemo();
		
		Runnable readRunnable = new Runnable() {
			
			@Override
			public void run() {
				try {
					demo.handleRead(readLock);
					// demo.handleRead(lock);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		};
		
		Runnable writeRunnable = new Runnable() {
			
			@Override
			public void run() {
				try {
					demo.handleWrite(writeLock, new Random().nextInt());
					// demo.handleWrite(lock, new Random().nextInt());
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		};

		long date = System.currentTimeMillis();
		for(int i=0; i<18; i++) {
			new Thread(readRunnable).start();
		}
		
		for(int i=18; i<20; i++) {
			new Thread(writeRunnable).start();
			new Thread(writeRunnable).start();
		}
		end.await();
		System.out.println("耗费时间:" + (System.currentTimeMillis()-date));
	}
}

              4)阻塞队列和生产者和消费者模型

               有界缓冲区(bounded-buffer)问题,阻塞队列 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue(无界队列)。

               PriorityBlockingQueue是一个无界队列,它没有限制,在内存允许的情况下可以无限添加元素;它又是具有优先级的队列,是通过构造函数传入的对象来判断,传入的对象必须实现comparable接口。

/**
 * 队列 + sychronized + wait()/notifyAll()实现生产者和消费者
 * @author TSjia
 *
 */

public class ProduceAndConsumerDemo {
	
	public static Object lock = new Object();

	static class Produce implements Runnable {

		Queue<Integer> queue = null;
		
		public Produce(Queue<Integer> queue) {
			this.queue = queue;
		}

		@Override
		public void run() {
			try {
				synchronized (lock) {
					while(queue.size() == 5) {
						lock.wait();
					}
					Integer i = new Random().nextInt(100);
					System.out.println("生产者--生产:" + i);
					queue.offer(i);
					lock.notifyAll();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	static class Consumer implements Runnable {

		Queue<Integer> queue = null;
		
		public Consumer(Queue<Integer> queue) {
			this.queue = queue;
		}

		@Override
		public void run() {

			try {
				synchronized (lock) {
					while(queue.size() == 0) {
						lock.wait();
					}
					System.out.println("消费者--取队列首元素:" + queue.poll());
					lock.notifyAll();
				}						
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args) {
		
		Queue<Integer> queue = new LinkedList<Integer>();
		
		Produce produce = new Produce(queue);		
		Consumer consumer = new Consumer(queue);
		
		for(int i=0; i<100; i++) {
			Thread t1 = new Thread(produce);
			t1.start();

			Thread t2 = new Thread(consumer);
			t2.start();
		}
	}
}

 

      4.3 同步工具类

              1)闭锁:CountDownLatch;

              用于控制主线程等待其他线程执行结束,再继续执行。

public class CountDownLatchDemo2 {

    public static void main(String[] args) throws InterruptedException {

        CountDownLatchDemo2 bisuo = new CountDownLatchDemo2();
        
        long threadtime = bisuo.timetasks(10);
        
        double ans = (double)threadtime / 1000000000;
        System.out.println("Thread time:" +ans + "s");
    }
	
    public long timetasks(int nThreads) throws InterruptedException {
        
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate = new CountDownLatch(nThreads);
        
        for (int i=0; i< nThreads; i++){
            Thread t = new Thread(){
                public void run(){
                    try{
                        startGate.await(); //wait the startGate count down to zero, then run.
                        try{
                        	Thread.sleep(1000);
                        	System.out.println("Thread Id: " + this.getId());
                        } finally {
                            endGate.countDown();
                        }
                    } catch (InterruptedException ignored){ }
                }
            };
            t.start();
        }
        long start = System.nanoTime();  //单位纳秒
        startGate.countDown();
        endGate.await();                 //wait the endGate count down to zero, then run.
        long end = System.nanoTime();
        return end - start;
    }
}

              2)Future;

              核心思想时异步调用,当调用一个耗时方法时,后台运行该方法,主线程继续处理其他任务,再获得耗时方法的结果。

// Callable 具体实现
public class TrueData implements Callable<String>{
	
	private String queryStr;
	
	public TrueData(String queryStr) {
		this.queryStr = queryStr;
	}

	@Override
	public String call() throws Exception {
		String result = "";
		if(queryStr.equals("诸葛亮")) {
			try {
				System.out.println("诸葛亮还没睡醒,请等待。。。");
				Thread.sleep(2000);
			} catch (Exception e) {
				e.printStackTrace();
			}
			result = "诸葛亮刚刚睡醒!";
		}
		else
			result = "我不是诸葛亮,我是他的弟弟诸葛均";
		
		return result;
	}
}
// FutureMain 函数
public class FutureMain {

	public static void main(String[] args) throws InterruptedException, ExecutionException{
		
		// submit提交一个实现 Callable 接口的任务,并且返回封装了异步计算结果的Future。
		ExecutorService executor = Executors.newFixedThreadPool(1);
		Future<String> future = executor.submit(new TrueData("诸葛亮"));

		System.out.println("到达卧龙岗");
		System.out.println("三顾茅庐 :" + future.get());
		
		try {
			Thread.sleep(3000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("三顾茅庐 :" + future.get());
		executor.shutdown();
	}
}

              3)信号量:Semaphore

              指定多个线程同时访问同一个资源。

public class SemaphoreDemo {

    public static void main(String[] args) {

        // 只能5个线程同时访问  
        final Semaphore semp = new Semaphore(5);
        
        for (int index = 0; index < 20; index++) {
            final int NO = index;
            new Thread() {
                public void run() {
                    try {
                        semp.acquire();  // 获取许可  
                        System.out.println("Accessing: " + NO);
                        Thread.sleep(1000);  // 模拟实际业务逻辑
                        semp.release();  // 访问完后,释放  
                    } catch (InterruptedException e) {
                    }
                }
            }.start();
        }
    }
} 

              4)栅栏:CyclicBarrier

              实现多个线程相互等待,直到所有线程都结束之后,再运行后续程序。

class CyclicBarrierWorker implements Runnable {
    private int id;
    private CyclicBarrier barrier;
 
    public CyclicBarrierWorker(int id, final CyclicBarrier barrier) {
        this.id = id;
        this.barrier = barrier;
    }
 
    @Override
    public void run() {
        try {
            System.out.println(id + " th people wait");
            barrier.await(); // 大家等待最后一个线程到达
	        } catch (InterruptedException | BrokenBarrierException e) {
	            e.printStackTrace();
	        }
	    }
	}
 
	public class TestCyclicBarrier {
	    public static void main(String[] args) {
	        int num = 10;
	        CyclicBarrier barrier = new CyclicBarrier(num, new Runnable() {
	            @Override
	            public void run() {
	                // TODO Auto-generated method stub
                System.out.println("go on together!");
            }
        });
        for (int i = 1; i <= num; i++) {
            new Thread(new CyclicBarrierWorker(i, barrier)).start();
        }
    }
}

      4.4 Executor

               1)继承关系

Executor的UML图

               2)四种常见的线程池:

                      CachedThreadPool:可缓存的线程池;

                      SecudleThreadPool:周期性执行任务的线程池;

                      SingleThreadPool:只有一条线程来执行任务;

                      FixedThreadPool:定长的线程池。


               3)ThreadPoolExecutor 类构造函数

public ThreadPoolExecutor(int corePoolSize,  
                              int maximumPoolSize,  
                              long keepAliveTime,  
                              TimeUnit unit,  
                              BlockingQueue<Runnable> workQueue,  
                              ThreadFactory threadFactory,  
                              RejectedExecutionHandler handler)

五、并发设计模式

      5.1 Future 模式(见4.3- 2))

      5.2 生产者消费者模式(见4.2- 4))

      5.3 单例模式

      5.4 CopyOnWrite(比如:String)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值