JUC并发编程 第一季

1、JUC java.util.concurrent 并发编程工具类

  • java.util.concurrent
  • java.util.concurrent.atomic
  • java.util.concurrent.locks

1.1 、进程及线程

进程:操作系统中运行的每一个程序就是一个进程。
线程:是进程中的一条执行路径,一个进程中可以有多个线程。

1.2 、并发及并行

并发:多线程同一时间点访问同一个资源。
并行:同时进行多个事件,泡着脚打王者荣耀,美滋滋。。。

1.3 、高内聚低耦合

高内聚:系统的架构的完整尽可能少的依赖外部资源,单个系统的维护、运行等尽可能低的影响、依赖于外部其它系统。
低耦合:系统内部各个模块间的依赖关系尽可能的低,各个模块的维护的影响的范围小,整个系统的灵活性提高(系统的改变,即各个模块的增、删、改容易),降低系统的维护成本,能尽可能的提高系统的多功能性。系统变更成本低,具备实现多功能的条件。
一个好的系统应该是分成各个小块,分而治之的思想,把复杂问题分成小问题,逐个击破。每个小块应该是高内聚,小块之间应该是低耦合的。高内聚说的是这个小块的功能已经不可分割了,已经足够简单。耦合说的是这个小块依赖其他小块提供的功能,低就是少,弱,高耦合就是多,而且强。

1.4、 多线程买票

版本一

package com.mace.juc;

/**
 * 资源类
 * 
 * @author 10836
 *
 */
class Ticket {
	private int ticket = 30;

	public synchronized void saleTicket() {
		if (ticket > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "-正在售出:" + ticket-- + ",剩余:" + ticket);
		}
	}
}

/**
 * 多线程售票
 * 
 * @author 10836 线程 操作 资源类
 */
public class SaleTicket {
	public static void main(String[] args) {
		Ticket t = new Ticket();

		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int x = 0; x < 40; x++) {
					t.saleTicket();
				}
			}
		}, "A").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int x = 0; x < 40; x++) {
					t.saleTicket();
				}
			}
		}, "B").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int x = 0; x < 40; x++) {
					t.saleTicket();
				}
			}
		}, "C").start();

	}
}

版本二

package com.mace.juc;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 资源类
 * 
 * @author 10836
 *
 */
class Ticket2 {
	private int ticket = 30;
	//可重入锁
	private final ReentrantLock lock = new ReentrantLock();
	
	public void saleTicket() {
		
			lock.lock();//加锁
			try {
				if (ticket > 0) {
					System.out.println(Thread.currentThread().getName() + "-正在售出:" + ticket-- + ",剩余:" + ticket);
				}
			} catch (Exception e) {
				e.printStackTrace();
				// TODO Auto-generated catch block
			}finally {
				lock.unlock();//解锁
			}
	}
}

/**
 * 多线程售票
 * 使用可重入锁及lambda表达式
 * @author 10836 线程 操作 资源类
 */
public class SaleTicket2 {
	public static void main(String[] args) {
		Ticket2 t = new Ticket2();
		//lambda表达式改写
		new Thread(()->{for (int x = 0; x < 40; x++) t.saleTicket();},"A").start();
		new Thread(()->{for (int x = 0; x < 40; x++) t.saleTicket();},"B").start();
		new Thread(()->{for (int x = 0; x < 40; x++) t.saleTicket();},"C").start();
	}
}

1.5、wait和sleep的区别

wait()方法会释放持有的锁,不然其他线程不能进入同步方法或同步块,从而不能调用notify(),notifyAll()方法来唤醒线程,产生死锁,所以释放锁,可以执行其他线程,也可以唤醒自己,只是设置停止自己的时间时不确定的;sleep方法不会释放持有的锁,设置sleep的时间是确定的会按时执行的;
sleep()必须指定时间,wait()可以指定时间也可以不指定;sleep()时间到,线程处于临时阻塞或运行状态;

1.6、线程的状态

NEW
实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
RUNNABLE
实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
调用线程的start()方法,此线程进入就绪状态。当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。锁池里的线程拿到对象锁后,进入就绪状态。
BLOCKED
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
WAITING
处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
TIMED_WAITING
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
TERMINATED
当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

在这里插入图片描述

1.7、 lambda表达式

  • 接口中只有一个方法的声明(函数式接口@FunctionalInterface)
  • 拷贝小括号,写死右箭头,落地大括号。
  • 函数式接口中可以有多个default默认方法
  • 函数式接口中可以有多个static静态方法
package com.mace.lamdba;

import java.util.UUID;

/**
 * lambda表达式
 * @author 10836
 *
 */
@FunctionalInterface
interface Say{
	
	void sayGood(String name);
	
	default int count(int x,int y) {return x+y;}
	
	static String uuid() {
		return UUID.randomUUID().toString();
	}
}

public class FirstLamdba {
	public static void main(String[] args) {
		
		Say s = (name)->{System.out.println(name +" say GOOD");};
		s.sayGood("bob");
		
		System.out.println(s.count(1, 2));
		
		System.out.println(Say.uuid());
		
	}
}

面向接口编程:
接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类。“面向接口编程”中的接口是一种思想层面的用于实现多态性、提高软件灵活性和可维护性的架构部件,而具体语言中的“接口”是将这种思想中的部件具体实施到代码里的手段。
抽象类和接口的区别:
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果。
7、抽象类里可以没有抽象方法。
8、如果一个类里有抽象方法,那么这个类只能是抽象类。
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。

1.8、 线程间的通信

生产者消费者基础版

package com.mace.juc;
/**
 * 线程通信测试
 * @author 10836
 * 有两个线程,操作初始值为0的一个变量
 * 一个线程对该变量+1,一个线程对该变量-1,
 * 实现交替10轮,变量值仍为0;
 * 
 * 高内聚低耦合 线程操作资源类
 */
class AirCon{
	private int num = 0 ;
	//增加
	public synchronized void incr() throws InterruptedException {
		if(num != 0 ) {
			this.wait();
		}
		num++;
		System.out.println(Thread.currentThread().getName()+"-生产\t"+num);
		this.notifyAll();
	}
	//减少
	public synchronized void decr() throws InterruptedException {
		if(num ==0) {
			this.wait();
		}
		num--;
		System.out.println(Thread.currentThread().getName()+"-消费\t"+num);
		this.notifyAll();
	}
}


public class ThreadNotfiyWait {
	public static void main(String[] args) {
		AirCon air = new AirCon();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.incr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"A").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.decr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"B").start();
	}
}

线程通信防止虚假唤醒

package com.mace.juc;

/**
 * 线程通信测试
 * @author 10836
 * 有两个线程,操作初始值为0的一个变量
 * 一个线程对该变量+1,一个线程对该变量-1,
 * 实现交替10轮,变量值仍为0;
 * 
 * 高内聚低耦合 线程操作资源类
 * 判断、通知、干活
 * 多线程交互中,必须防止多线程的虚假唤醒,也即(判断只能用while不能用if)
 * 
 */

class AirCon{
	private int num = 0 ;
	//增加
	public synchronized void incr() throws InterruptedException {
		while(num != 0 ) {
			this.wait();
		}
		num++;
		System.out.println(Thread.currentThread().getName()+"-生产\t"+num);
		this.notifyAll();
	}
	//减少
	public synchronized void decr() throws InterruptedException {
		while(num ==0) {
			this.wait();
		}
		num--;
		System.out.println(Thread.currentThread().getName()+"-消费\t"+num);
		this.notifyAll();
	}
}


public class ThreadNotfiyWait {
	public static void main(String[] args) {
		AirCon air = new AirCon();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.incr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"A").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.incr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"B").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.decr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"C").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.decr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"D").start();
	}
}

线程通信新写法:

package com.mace.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程通信测试-新写法
 * @author 10836
 * 有两个线程,操作初始值为0的一个变量
 * 一个线程对该变量+1,一个线程对该变量-1,
 * 实现交替10轮,变量值仍为0;
 * 
 * 高内聚低耦合 线程操作资源类
 * 判断、通知、干活
 * 多线程交互中,必须防止多线程的虚假唤醒,也即(判断只能用while不能用if)
 * 
 */

class AirCon2{
	private int num = 0 ;
	private final ReentrantLock lock = new ReentrantLock(); 
	private final Condition con = lock.newCondition();
	
	//增加
	public void incr() throws InterruptedException {
		lock.lock();
		try {
			while(num != 0 ) {//if
				con.await();//this.wait();
			}
			num++;
			System.out.println(Thread.currentThread().getName()+"-生产\t"+num);
			con.signalAll();//this.notifyAll();
		} finally {
			lock.unlock();
		}
	}
	//减少
	public void decr() throws InterruptedException {
		lock.lock();
		try {
			while(num ==0) {//if
				con.await();//this.wait();
			}
			num--;
			System.out.println(Thread.currentThread().getName()+"-消费\t"+num);
			con.signalAll();//this.notifyAll();
		} finally {
			lock.unlock();
		}
	}
}


public class ThreadNotfiyWait2 {
	public static void main(String[] args) {
		AirCon2 air = new AirCon2();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.incr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"A").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.incr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"B").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.decr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"C").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ;i++) {
				try {
					air.decr();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		},"D").start();
	}
}

线程通信精确唤醒:

package com.mace.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class PrintThread{
	
	private int flag = 1;//1=A 2=B 3=C
	private final Lock lock = new ReentrantLock();
	//一道门 三把锁
	private final Condition con1 = lock.newCondition();
	private final Condition con2 = lock.newCondition();
	private final Condition con3 = lock.newCondition();
	
	public void print5() {
		lock.lock();
		try {
			while(flag != 1) {
				try {
					con1.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			for(int i = 1 ; i <= 5;i++) {
				System.out.println(Thread.currentThread().getName()+"\t"+i);
			}
			flag = 2;
			con2.signal();
		
		} finally{
			lock.unlock();
		}
	}
	
	public void print10() {
		lock.lock();
		try {
			while(flag != 2) {
				try {
					con2.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			for(int i = 1 ; i <= 10;i++) {
				System.out.println(Thread.currentThread().getName()+"\t"+i);
			}
			flag = 3;
			con3.signal();
		
		} finally{
			lock.unlock();
		}
	}
	
	public void print15() {
		lock.lock();
		try {
			while(flag != 3) {
				try {
					con3.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			for(int i = 1 ; i <= 15;i++) {
				System.out.println(Thread.currentThread().getName()+"\t"+i);
			}
			flag = 1;
			con1.signal();
		
		} finally{
			lock.unlock();
		}
	}
}

/**
 * 线程间通信精确通知
 * @author 10836
 * 多线程之间按顺序调用,实现A->B->C->三个线程启动:
 * 
 * A 打印5次
 * B 打印10次
 * C 打印15次
 * 这样打印10轮。
 * 
 * 高内聚低耦合 线程、操作、资源类
 * 判断、通知、干活
 * 多线程交互中,必须防止多线程的虚假唤醒,也即(判断只能用while不能用if)
 * 标志位
 */
public class ThreadOrderAccess {
	public static void main(String[] args) {
		PrintThread pt = new PrintThread();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ; i++) {
				pt.print5();
			}
		},"A").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ; i++) {
				pt.print10();
			}
		},"B").start();
		
		new Thread(()->{
			for(int i = 0 ; i < 10 ; i++) {
				pt.print15();
			}
		},"C").start();
		
	}
}

2、锁

在这里插入图片描述
在这里插入图片描述
synchronized 实现同步:

  • 普通方法锁是当前实例对象
  • 静态方法锁是类的Class对象
  • 同步代码块是当前实例对象
package com.mace.juc;

import java.util.concurrent.TimeUnit;

class Phone{
	
	public synchronized void sendEmail() {
		System.out.println("send email。。。。");
	}
	
	public synchronized void sendSMS() {
		System.out.println("send SMS。。。。");
	}
}


/**
 * lock机制
 * @author 10836
 *
 */
public class Lock {
	public static void main(String[] args) throws InterruptedException {
		Phone p = new Phone();
		
		new Thread(()->{
			p.sendEmail();
		},"A").start();
		
		TimeUnit.MILLISECONDS.sleep(100);
		
		new Thread(()->{
			p.sendSMS();
		},"A").start();
	}
}

3、list不安全

CopyOnWrite写时复制技术,是一种读写分离的思想,读写不同的容器。

package com.mace.collect;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ArrayList 是线程不安全的
 * @author 10836
 * java.util.ConcurrentModificationException并发修改异常
 * 导致原因:
 * 解决方案:
 * 	1.Vector线程安全
 *  2.Collections.synchronizedList(new ArrayList<>())
 *  3.new CopyOnWriteArrayList<>()
 * 优化建议:使用线程安全容器
 */
public class ThreadList {
	public static void main(String[] args) throws Exception {
		//List<String> list = new ArrayList<>();
		//List<String> list = new Vector<>();
		//List<String> list = Collections.synchronizedList(new ArrayList<>());
		List<String> list = new CopyOnWriteArrayList<String>(); 
		
//	    CopyOnWriteArrayList源码	
//	    public boolean add(E e) {
//	        final ReentrantLock lock = this.lock;
//	        lock.lock();
//	        try {
//	            Object[] elements = getArray();
//	            int len = elements.length;
//	            Object[] newElements = Arrays.copyOf(elements, len + 1);复制
//	            newElements[len] = e;
//	            setArray(newElements);
//	            return true;
//	        } finally {
//	            lock.unlock();
//	        }
//	    }
		
		for(int i = 0 ; i < 30;i++) {
			new Thread(()->{
				list.add(UUID.randomUUID().toString().substring(0, 5));
				System.out.println(list);
			},String.valueOf(i)).start();
		}
		
		TimeUnit.SECONDS.sleep(5);
		
		System.out.println(list);
		
	}
}

4、set不安全

package com.mace.collect;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class ThreadSet {
	public static void main(String[] args) {
		//Set<String> set =new HashSet<>();线程不安全
		//Set<String> set = Collections.synchronizedSet(new HashSet<>());
		Set<String> set = new CopyOnWriteArraySet<String>();
		
		for(int i = 0 ; i < 30;i++) {
			new Thread(()->{
				set.add(UUID.randomUUID().toString().substring(0, 5));
				System.out.println(set);
			},String.valueOf(i)).start();
		}
	}
}

//实际上是HashMap
public HashSet() {
 	map = new HashMap<>();
}
//添加的值存储在HashMap的key,HashMap的value为new Object()
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
    //private static final Object PRESENT = new Object();
}

5、map不安全

package com.mace.collect;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class ThreadMap {
	public static void main(String[] args) {
		//HashMap<String,String> map = new HashMap<>();
		//Map<Object, Object> map = Collections.synchronizedMap(new HashMap<Object,Object>());
		Map<Object, Object> map = new ConcurrentHashMap<Object, Object>();
		
		for(int i = 0 ; i < 30;i++) {
			new Thread(()->{
				map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 5));
				System.out.println(map);
			},String.valueOf(i)).start();
		}
	}
}

6、callable创建线程

package com.mace.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
 * callable创建多线程
 * @author 10836
 */
class MyThread implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		System.out.println(Thread.currentThread().getName());
		return 1;
	}
}

public class CallableThread {
	public static void main(String[] args) throws Exception {
		FutureTask<Integer> task = new FutureTask<>(new MyThread());
		new Thread(task, "A").start();
		System.out.println(task.get());
	}
}

6.1 callable与runable的区别

  • 是否有返回值
  • 是否抛异常
  • 落地方法call和run

6.2 callable细节

  • get方法一般放在最后一行,否则将等待直到有返回值。
  • 多个线程同时调用FutureTask,首次得到返回值会进行缓存,其他线程不在重新调用。

7、多线程辅助工具类

7.1 、CountDownLatch加计数器

package com.mace.thread;

import java.util.concurrent.CountDownLatch;

/**
 * 主线程必须等到其他线程全部执行完再结束
 * @author 10836
 */
public class CountDownLatchDemo {
	public static void main(String[] args) throws InterruptedException {
		/**
		 * 当一个线程或多线程调用await方法时,线程会阻塞。
		 * 其他线程调用countDown方法会将计数器减一。
		 * 当计数器为0时,因await方法阻塞的线程会被唤醒,继续执行。
		 */
		CountDownLatch latch = new CountDownLatch(6);
		
		for (int i = 0; i < 6; i++) {
			new Thread(()->{
				System.out.println(Thread.currentThread().getName()+"\t离开教室...");
				latch.countDown();
			},String.valueOf(i)).start();
		}
		
		latch.await();
		System.out.println("班长离开教室...");
	}
}

7.2、CyclicBarrier减计数器

package com.mace.thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * 减法计数器
 * @author 10836
 */
public class CyclicBarrierDemo {
	public static void main(String[] args) {
		// public CyclicBarrier(int parties, Runnable barrierAction) 
		CyclicBarrier cb = new CyclicBarrier(7,()->{System.out.println("**召唤神龙**");});
		
		for (int i = 1; i < 8; i++) {
			final int temp = i;
			new Thread(()->{
				System.out.println("收集到"+temp+"龙珠...");
				try {
					cb.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (BrokenBarrierException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			},String.valueOf(i)).start();
		}
	}
}

7.3、Semaphore信号量

package com.mace.thread;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 信号量控制
 * @author 10836
 * 6辆车停3个车位上
 */
public class SemaphoreDemo {
	public static void main(String[] args) {
		/**
		 * acquire(获取)当一个线程调用acquire操作时,它要么成功的获取信号量(信号量减一)
		 * 要么一直等下去,直到有线程释放信号量,或超时。
		 * release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
		 * 目的:
		 * 	1.多个共享资源的互斥使用
		 *  2.并发线程数的控制
		 */
		Semaphore sp = new Semaphore(3);//三个车位
		
		for (int i = 1; i < 7; i++) {
			new Thread(()->{
				try {
					sp.acquire();//占住车位
					System.out.println(Thread.currentThread().getName()+"号车,停入车位。");
					TimeUnit.SECONDS.sleep(5);
					System.out.println(Thread.currentThread().getName()+"号车,驶离车位。");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally {
					sp.release();//释放车位
				}
			},String.valueOf(i)).start();
		}
	}
}

7.4 、ReadWriteLock读写锁

package com.mace.thread;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class MyCache{
	private volatile Map<String,String> map = new HashMap<>();
	private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
	
	
	public void set(String key,String value) {
		lock.writeLock().lock();
		try {
			System.out.println(Thread.currentThread().getName()+"\t开始写入:"+key);
			map.put(key, value);
			TimeUnit.MILLISECONDS.sleep(500);
			System.out.println(Thread.currentThread().getName()+"\t写入成功");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			lock.writeLock().unlock();
		}
	}
	
	public void get(String key) {
		lock.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName()+"\t开始读取");
			map.get(key);
			TimeUnit.MILLISECONDS.sleep(500);
			System.out.println(Thread.currentThread().getName()+"\t读取成功");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			lock.readLock().unlock();
		}
	}
	
}


/**
 * 多个线程同时读取一个资源类没有问题,所以为了满足并发量,
 * 读取共享资源可以同时进行。但是为了满足数据的一致性,写
 * 共享资源时就不应该再有其他线程可以对资源进行读或写。
 *   1.读-读能共存
 *   2.读-写不能共存
 *   3.写-写不能共存
 */
public class ReadWriteLcok {
	public static void main(String[] args) {
		MyCache cache = new MyCache();
		for (int i = 1; i < 6; i++) {
			final int temp = i;
			new Thread(()->{
				cache.set(String.valueOf(temp), String.valueOf(temp));
			},String.valueOf(i)).start();
		}
		
		for (int i = 1; i < 6; i++) {
			final int temp = i;
			new Thread(()->{
				cache.get(String.valueOf(temp));
			},String.valueOf(i)).start();
		}
	}
}

8、BlockingQueue阻塞队列

必须要阻塞/不得不阻塞, FIFO (first-in-first-out) 。
在这里插入图片描述

  • 当队列是空的,从队列中获取元素的操作会被阻塞。
  • 当队列是满的,往队列中添加元素的操作会被阻塞。

阻塞队列类型
在这里插入图片描述
主要方法:
在这里插入图片描述

package com.mace.thread;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * 阻塞队列
 * @author 10836
 */
public class BlockQueueDemo {
	public static void main(String[] args) {
		BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
		System.out.println(queue.add("A"));
		System.out.println(queue.add("B"));
		System.out.println(queue.add("C"));
		
		System.out.println(queue.remove());
		System.out.println(queue.remove());
		System.out.println(queue.remove());
		//java.util.NoSuchElementException 超出容量后抛出异常
		System.out.println(queue.remove());
	}
}

9、线程池

9.1、线程池的优势

在这里插入图片描述
(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

9.2、使用

package com.mace.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 线程池
 * @author 10836
 *
 */
public class ThreadPool {
	public static void main(String[] args) throws InterruptedException {
		//ExecutorService pool = Executors.newFixedThreadPool(5);//固定线程数
		//ExecutorService pool = Executors.newSingleThreadExecutor();//只有一个线程
		ExecutorService pool = Executors.newCachedThreadPool();//只有一个线程
		
		for(int i = 1 ; i < 11;i++) {
			final Integer temp = i;
			pool.execute(()->{
				System.out.println(Thread.currentThread().getName()+"处理第"+temp+"顾客。。。");
			});
			//TimeUnit.SECONDS.sleep(1);
		}
	}
}

9.3、线程池参数

在这里插入图片描述

/*	corePoolSize:线程池中的常驻核心线程数
    maximumPoolSize:线程池中能够容纳同时执行的最大线程数,此值必须大于1.
    keepAliveTime:多余的空闲线程的存活时间,超过corePoolSize的线程,将在keepAliveTime到达时销毁。
    unit:keepAliveTime的时间单位。
    workQueue:任务队列,被提交但尚未被执行的任务。
    threadFactory:生产线程中工作线程的线程工厂,一般默认即可。
    handler:拒绝策略,当队列满了,并且工作线程大于等于线程池的maximumPoolSize时如何拒绝请求执行的策略。
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
*/

9.4、线程池工作原理

在这里插入图片描述
在这里插入图片描述

9.5、线程池的拒绝策略

    new ThreadPoolExecutor.AbortPolicy():抛出异常RejectedExecutionException
    new ThreadPoolExecutor.CallerRunsPolicy():将任务返回给调用者
    new ThreadPoolExecutor.DiscardPolicy():丢弃无法处理的任务
    new ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列中等待最久的任务

9.6、线程池参数优化设计

  • cpu密集型
  • io密集型

10、函数式接口

在这里插入图片描述

package com.mace.lamdba;

import java.util.function.Consumer;

/**
 * 函数式接口测试
 * @author 狼牙
 */
public class FunctionInterFaceDemo {
	public static void main(String[] args) {
//		Consumer<String> comsumer = new Consumer<String>() {
//			@Override
//			public void accept(String t) {
//			}
//		};
		//消费型接口,有输入没有返回值。
		Consumer<String> comsumer = (t)->{System.out.println(t);};
		comsumer.accept("JAVA");
	}
}

11、stream流式编程

package com.mace.stream;

import java.util.Arrays;
import java.util.List;

/**
 * stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
 * 集合讲的是数据,流将的是计算。
 * 特点:
 * 	1.自己不会存储元素
 *  2.不会改变源对象,会返回一个持有结果的新stream
 *  3.stream操作是延迟执行的,需要结果的时候才执行。
 * 操作:
 * 	1.创建一个数据源
 *  2.中间操作,处理数据源
 *  3.终止操作,执行中间操作链,产生结果。
 * @author 狼牙
 *
 */

class User{
	private String name;
	private int age;
	
	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "User [name=" + name + ", age=" + age + "]";
	}
}


public class StreamDemo {
	public static void main(String[] args) {
		User u1 = new User("a",18);
		User u2 = new User("b",22);
		User u3 = new User("c",29);
		
		List<User> list = Arrays.asList(u1,u2,u3);
		
		list.stream().map(u->{
			return u.getName().toUpperCase();
		}).forEach(System.out::println);
		
	}
}

12、ForkJoin框架

package com.mace.forkjoin;
/**
 * 分支合并框架
 * @author 狼牙
 * ForkJoinPool
 * ForkJoinTask抽象类 -> RecursiveTask实现类
 */

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * 计算0-100的和
 * @author 10836
 *
 */
class MyTask extends RecursiveTask<Integer>{
	
	private static final int FLAG = 10;
	
	private int start;
	private int end;
	private int result;
	
	public MyTask(int start, int end) {
		super();
		this.start = start;
		this.end = end;
	}


	@Override
	protected Integer compute() {
		if((end-start) <= FLAG) {
			for(int i = start;i<=end;i++) {
				result = result + i;
			}
		}else {//如果小于10继续拆分
			int middle = (start + end)/2;
			MyTask m1 = new MyTask(start,middle);
			MyTask m2 = new MyTask(middle+1,end);
			m1.fork();
			m2.fork();
			Integer j1 = m1.join();
			Integer j2 = m2.join();
			result = j1+j2;
		}
		return result;
	}
}


public class ForkJoinDemo {
	public static void main(String[] args) throws Exception {
		ForkJoinPool pool = new ForkJoinPool();
		ForkJoinTask<Integer> task = pool.submit(new MyTask(0, 100));
		System.out.println(task.get());
		pool.shutdown();
	}
}

12.1、异步回调

package com.mace.forkjoin;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * 异步回调
 * @author 10836
 */
public class CompleTableFutureDemo {
	public static void main(String[] args) throws Exception {
		CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
			System.out.println("异步操作没有返回值...");
		});
		future.get();//调用get方法才会执行
		
		
		CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(()->{
			System.out.println("异步操作,有返回值....");
			//int x = 1/0;
			return 666;
		});
		
		Integer integer = supplyAsync.whenComplete((t,u)->{
			//当异步任务正常执行,t为任务的返回值,u为异常对象,为null。
			//当异步任务出现异常,则t为null,u为异常对象。
			System.out.println("**"+t);
			System.out.println("--"+u);
		}).exceptionally((t)->{
			//当异步任务正常执行,次方法不会被调用。
			//当异步任务出现异常,t为异常对象,并且有返回值。
			System.out.println("++"+t);
			return 999;
		}).get();
		
		System.out.println(integer);
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值