Java高级多线程

线程池概念

• 现有问题
• 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。
• 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。
• 线程池:
• 线程容器,可设定线程分配的数量上限。
• 将预先创建的线程对象存入池中,并重用线程池中的线程对象。
• 避免频繁的创建和销毁。

线程池原理

将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程。

获取线程池

• 常用的线程池接口和类(所在包java.util.concurrent)
• Executor:线程池的顶级接口。
• ExecutorService:线程池接口,可通过submit(Runnable task) 提交任务代码。
• Executors工厂类:通过此类可以获得一个线程池。
• 通过 newFixedThreadPool(int nThreads) 获取固定数量的线程池。
参数:指定线程池中 线程的数量。
• 通过newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,没有上限

Callable接口

public interface Callable<V>{ 
	public V call() throws Exception; 
	}

• JDK5加入,与Runnable接口类似,实现之后代表一个线程任务。
• Callable具有泛型返回值、可以声明异常

Future接口

• 概念
异步接收ExecutorService.submit()所返回的状态结果,当中包含了 call()的返回值
• 方法:
V get()以阻塞形式等待Future中的异步处理结果(call()的返回值)

线程的同步、异步

• 同步:
• 形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续。
在这里插入图片描述
• 注:单条执行路径。
•异步:
• 形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回。 二者竞争时间片,并发执行。
在这里插入图片描述
• 注:多条执行路径。

Lock接口

• JDK5加入,与synchronized比较,显示定义,结构更灵活。
• 提供更多实用性方法,功能更强大、性能更优越。
• 常用方法:
void lock() //获取锁, 如锁被占用,则等待。
boolean tryLock() //尝试获取锁( 成功返回true。失败返回false,不阻塞)
void unlock() //释放锁

重入锁

• ReentrantLock:
Lock接口的实现类,与synchronized一样具有互斥锁功能
在这里插入图片描述

读写锁

• ReentrantReadWriteLock:
• 一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
• 支持多次分配读锁,使多个读操作可以并发执行。
• 互斥规则:
• 写-写:互斥,阻塞。
• 读-写:互斥,读阻塞写、写阻塞读。
• 读-读:不互斥、不阻塞。
• 在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
public class TestReadWriteLock {
	public static void main(String[] args) {
		Student stu = new Student();//共享资源对象
		ExecutorService es = Executors.newFixedThreadPool(20);
		WriteTask1 write = new WriteTask1(stu);//写线程任务
		ReadTask read = new ReadTask(stu);//读线程任务
		//执行的两个赋值的线程任务
		long start = System.currentTimeMillis();//开始时间  毫秒值
		es.submit(write);
		es.submit(write);
		for(int i =1;i<=18;i++) {
			es.submit(read);
		}
		//停止线程池,但是不停止已提交的任务!等已提交任务都执行完毕!
		es.shutdown();
		//询问线程池,任务结束了吗?
		while(true) {
			System.out.println("结束了吗?");
			if(es.isTerminated() == true) {//就证明线程池里的任务都执行完毕了!
				break;
			}
		}
		long end = System.currentTimeMillis();//结束时间
		System.out.println(end - start);
	}
}
//写操作任务
class WriteTask1 implements Callable{
	Student stu;
	public WriteTask1(Student stu) {
		this.stu = stu;
	}
	@Override
	public Object call() throws Exception {
		stu.setAge(100);
		return null;
	}
}
//读操作任务
class ReadTask implements Callable{
	Student stu;
	public ReadTask(Student stu) {
		this.stu = stu;
	}
	@Override
	public Object call() throws Exception {
		stu.getAge();
		return null;
	}
}
class Student{
	private int age;
	//	Lock lock = new ReentrantLock();//写和读的操作下,都加锁!性能过低!
	ReentrantReadWriteLock rrwl = new 
	ReentrantReadWriteLock();//有两把锁
	ReadLock read = rrwl.readLock();//读锁
	WriteLock write = rrwl.writeLock();//写锁
	//赋值---写操作!
	public void setAge(int age) {
		write.lock();
		try {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.age = age;
		}finally {
			write.unlock();
		}
	}
	//取值---读操作
	public int getAge() {
		read.lock();
		try {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return this.age;
		}finally {
			read.unlock();
		}
	}	
}

线程安全的集合

• Collection体系集合下,除Vector以外的线程安全集合。

Collections中的工具方法

• Collections工具类中提供了多个可以获得线程安全集合的方法。
• public static < T > Collection< T > synchronizedCollection(Collection< T >c)
//返回指定 collection 支持的同步(线程安全的)collection
• public static < T > List< T > synchronizedList(List< T > list)
//返回指定 collection 支持的同步(线程安全的)collection
• public static < T > Set< T > synchronizedSet(Set< T > s)
//返回指定 set 支持的同步(线程安全的)set
• public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
//返回由指定映射支持的同步(线程安全的)映射
• public static < T> SortedSet< T > synchronizedSortedSet(SortedSet< T > s)
//返回指定有序 set 支持的同步(线程安全的)有序 set
• public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
//返回指定有序映射支持的同步(线程安全的)有序映射
• JDK1.2提供,接口统一、维护性高,但性能没有提升,均以synchonized实现。

CopyOnWriteArrayList

• 线程安全的ArrayList,加强版读写分离。
• 写有锁,读无锁,读写之间不阻塞,优于读写锁。
• 写入时,先copy一个容器副本、再添加新元素,最后替换引用。
• 使用方式与ArrayList无异。

public class TestCopyOnWriteArrayList {
	public static void main(String[] args) {
		//写有锁, 读无锁的集合。
		CopyOnWriteArrayList<String> alist = new CopyOnWriteArrayList<String>();
		//写操作  有锁
		alist.add("A");//都将底层数组做了一次复制,写的是新数组,完成赋值后,再将新数组替换掉旧数组!
		alist.add("B");//每调用一次,底层方法扩容一次!
		//读操作   无锁
		alist.get(1);//读的是写操作完成之前的旧数组!写完之后,才能读到的新数组的新值
	}
}

CopyOnWriteArraySet

• 线程安全的Set,底层使用CopyOnWriteArrayList实现。
• 唯一不同在于,使用addIfAbsent()添加元素,会遍历数组,
• 如存在元素,则不添加(扔掉副本)。

public class TestCopyOnWriteArraySet {
	public static void main(String[] args) {
		//无序、无下标、不允许重复
		CopyOnWriteArraySet<String> aset = new CopyOnWriteArraySet<String>();
		//写操作  表面使用的是add方法,底层实际是用的CopyOnWriteArrayList的 addIfAbsent()来判断要插入的新值是否存在
		aset.add("A"); 
		aset.add("B");
		aset.add("C");
		
		for(String s : aset) {
			System.out.println(s);
		}
	}
}

ConcurrentHashMap

• 初始容量默认为16段(Segment),使用分段锁设计。
• 不对整个Map加锁,而是为每个Segment加锁。
• 当多个对象存入同一个Segment时,才需要互斥。
• 最理想状态为16个对象分别存入16个Segment,并行数量16。
• 使用方式与HashMap无异。

public class TestConcurrentHashMap {
	public static void main(String[] args) {
HashMap<String,String> maps = new HashMap<String,String>();
		/*分段锁设计  Segment   JDK1.7的做法
		CAS交换算法和同步锁    
		同步锁锁的是表头对象,拿到锁的对象要先做节点遍历。
		查看有没有相同的key,相同覆盖;不同--则挂在最后一个节点的next上     JDK1.8的做法*/
		ConcurrentHashMap<String, String> ch = new ConcurrentHashMap<String, String>();
		
		ch.put("A","蓝轩");
		ch.keySet();
		}
}

Queue接口(队列)

• Collection的子接口,表示队列FIFO(First In First Out)
• 常用方法:
• 抛出异常:
• boolean add(E e) //顺序添加一个元素(到达上限后,再添加则会抛出异常)
• E remove() //获得第一个元素并移除(如果队列没有元素时,则抛异常)
• E element() //获得第一个元素但不移除(如果队列没有元素时,则抛异常)
• 返回特殊值:推荐使用
• boolean offer(E e) //顺序添加一个元素 (到达上限后,再添加则会返回false)
• E poll() //获得第一个元素并移除 (如果队列没有元素时,则返回null)
• E keep() //获得第一个元素但不移除 (如果队列没有元素时,则返回null)

ConcurrentLinkedQueue

• 线程安全、可高效读写的队列,高并发下性能最好的队列。
• 无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)
• V:要更新的变量、E:预期值、N:新值。
• 只有当V==E时,V=N;否则表示已被更新过,则取消当前操作。

BlockingQueue接口(阻塞队列)

• Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法。
• 方法:
• void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待。
• E take() //获取并移除此队列头部元素,如果没有可用元素,则等待。
可用于解决生产生、消费者问题

阻塞队列

ArrayBlockingQueue: 数组结构实现,有界队列。(手工固定上限)
LinkedBlockingQueue
• 链表结构实现,无界队列。(默认上限Integer.MAX_VALUE)

public class TestQueue {
	public static void main(String[] args) {
		
//		Queue<String> q = new
		//列表,尾部添加(指定下标)
		//链表,头尾添加
		//队列,FIFO
		//如果要强制LinkedList只遵循队列的规则!
//		Queue<String> link = new LinkedList<String>(); //遵循队列规则的链表
		LinkedList<String> link = new LinkedList<String>();
		link.offer("A");
		link.offer("B");
		link.offer("C");	
		//用列表的方式打乱了FIFO队列的规则
//		link.add(0,"D");
		//强制LinkedList后,不能调用带有下标的add方法
		System.out.println(link.peek());//队列中的第一个元素!
		//严格遵守了队列的规则,且是线程安全的,采用了CAS交换算法
		Queue<String> q = new ConcurrentLinkedQueue<String>();
		//1.抛出异常的   2.返回结果的
		q.offer("A");
		q.offer("B");
		q.offer("C");
		q.poll();//删除表头
		System.out.println(q.peek());//获得表头
		//手动固定队列上限
		BlockingQueue<String> bq = new ArrayBlockingQueue<String>(3);
		//无界队列  最大有 Integer.MAX_VALUE
		BlockingQueue<String> lbq = new LinkedBlockingQueue<String>();	
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值