线程池、Callable接口、Future接口、Lock接口、同步锁的使用方法
14.4 线程池
14.4.1 线程池概念
现有问题:
- 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出;
- 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降;
线程池:
- 线程容器,可设定线程分配的数量上限;
- 将预先创建的线程对象存入池中,并重用线程池中的线程对象 避免频繁的创建和销毁;
14.4.2 线程池原理
- 将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程;
14.4.3 获取线程池
常用的线程池接口和类(所在包java.util.concurrent)
- Executor:线程池的顶级接口
- ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码;
- Executors工厂类:通过此类可以获得一个线程池;
- 通过newFixedThreadPool(int nThreads) 获取固定数量的线程池。参数:指定线程池中线程的数量;
- 通过newCachedThreadPool() 获得动态数量的线程池,如不够则创建新的,没有上限;
14.4.4 Callable接口
public interface Callable<V>{
public V call() throws Exception;
}
- JDK5加入,与Runnable接口类似,实现之后代表一个线程任务;
- Callable具有泛型返回值、可以声明异常;
14.4.5 Future接口
- 概念:异步接收ExecutorService.submit() 所返回的状态结果,当中包含了call()的返回值;
- 方法:V get() 以阻塞形式等待Future中的异步处理结果(call()的返回值)
14.4.6 线程的同步
同步:
- 形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续;
- 注:单条执行路径;
14.4.7 线程的异步
异步:
- 形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回。二者竞争时间片,并发执行;
- 注:多条执行路径;
14.4.8 Lock接口
- JKD5加入,与synchronized比较,显示定义,结构更灵活;
- 提供更多实用性方法,功能更强大、性能更优越;
- 常用方法:
void lock() //获取锁,如锁被占用,则等待;
boolean tryLock() //尝试获取锁(成功返回true。失败返回false,不阻塞)
void unlock() //释放锁
14.4.8 重入锁
- ReentrantLock:Lock接口的实现类,与synchronized一样具有互斥锁功能;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLocks {
public static void main(String[] args) {
Test obj = new Test();
Thread t1 = new Thread(new MyTask(obj));
Thread t2 = new Thread(new MyTask2(obj));
t1.start();
t2.start();
}
}
class Test{
//接口引用指向实现类对象
Lock lock = new ReentrantLock();
//1.使用Lock,需要明确的写上锁和释放锁
//2.为了避免拿到锁的线程在运行期间出现异常,导致程序终止,没有释放锁,应用try{}finally{}来保证无论执行正确与否,最终都会释放锁
public void method(){
System.out.println(Thread.currentThread().getName()+"进入到上锁的方法里");
try {
lock.lock();//显示的协商在此次获得锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
System.out.println(Thread.currentThread().getName()+"退出了上锁的方法");
lock.unlock();//释放此处锁
}
}
}
class MyTask implements Runnable{
Test obj;
public MyTask(Test obj) {
this.obj = obj;
}
public void run() {
obj.method();
}
}
class MyTask2 implements Runnable{
Test obj;
public MyTask2(Test obj) {
this.obj = obj;
}
public void run() {
obj.method();
}
}
输出结果:
Thread-0进入到上锁的方法里
Thread-1进入到上锁的方法里
Thread-0退出了上锁的方法
Thread-1退出了上锁的方法
14.4.9 读写锁
- ReentrantReadWriteLock:
一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁;
支持多次分配读锁,使多个读操作可以并发执行; - 互斥规则:
写-写:互斥、阻塞;
读-写:互斥,读阻塞写、写阻塞读;
读-读:不互斥、不阻塞;
在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率;
14.4.10 ReentrantReadWriteLock
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
public class TestReadWroteLock {
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 = 0 ; i < 10 ; i++) {
es.submit(read);
}
//停止线程池,但不停止已提交的任务,等已提交任务都执行完毕
es.shutdown();
//询问线程池任务结束了吗
while(true) {
if(es.isTerminated() == true) {//任务都执行完毕
System.out.println("任务执行完毕");
break;
}
}
long end = System.currentTimeMillis();//结束时间
System.out.println(end - start+"毫秒");
}
}
class WriteTask1 implements Callable{
Student stu;
public WriteTask1(Student stu) {
this.stu = stu;
}
public Object call() throws Exception{
stu.setAge(100);
return null;
}
}
class ReadTask implements Callable{
Student stu;
public ReadTask(Student stu) {
this.stu = stu;
}
public Integer call() throws Exception{
return stu.getAge();
}
}
class Student{
private int age;
// Lock lock = new ReentrantLock();//写和读的操作都加锁,效率过低
ReentrantReadWriteLock rrw1 = new ReentrantReadWriteLock();//有两把锁
ReadLock readlock = rrw1.readLock();//读锁
WriteLock writelock = rrw1.writeLock();//写锁
//赋值--写操作
public void setAge(int age) {
writelock.lock();
try {
Thread.sleep(1000);//每写入一次,休眠1秒
this.age = age;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
writelock.unlock();
}
}
//取值--读操作
public int getAge() {
readlock.lock();
try {
try {
Thread.sleep(1000);//每写入一次,休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.age;
}finally {
readlock.unlock();
}
}
}
输出结果:
任务执行完毕
3016毫秒
- 运行结果:互斥锁运行时间20096毫秒,读写锁运行时间3016毫秒。(2次写各占1秒,18次读共占1秒)
来源:CSDN
作者:X池鱼
链接:https://blog.csdn.net/weixin_44257082/article/details/104850357