文章目录 线程池概念 线程池原理 创建线程池,线程池API Callable接口 Callable的基本使用 Callable结合线程池的使用 Future接口
文章目录
- 线程池概念
- 线程池原理
- 创建线程池,线程池API
- Callable接口
- Callable的基本使用
- Callable结合线程池的使用
- Future接口
- 线程同步和异步
- 线程同步
- 线程异步
- Lock接口(与synchronized)
- Lock接口的实现类:ReentrantLock
- ReadWriteLock接口:ReentrantReadWriteLock
线程池概念
- 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。
- 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。
- 线程容器,可设定线程分配的数量上限。
- 将预先创建的线程对象存入池中,并重用线程池中的线程对象。
- 避免频繁的创建和销毁。
线程池原理
将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程
创建线程池,线程池API
常用的线程池接口和类(所在包java. util. concurrent) :
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池的创建
* (1)Executor:线程池的根接口 -----> execute()执行
* (2)ExecutorService:包含了管理线程池的一些方法,submit,shutdown
* 子类 ThreadPoolExecutor
* 子类 ScheduledThreadPoolExecutor
* (3)Executors:创建线程池的工具类
* (3.1)创建固定线程个数线程池
* (3.2)创建缓存(动态数量)线程池,由任务的多少决定
* (3.3)创建单线程池
* (3.4)创建调度线程池,调度:周期、定时执行
*
*/
public class ThreadPoolDemo01 {
public static void main(String[] args) {
//1.1创建固定线程个数线程池
//ExecutorService executorService = Executors.newFixedThreadPool(4);
//1.2创建缓存(动态数量)线程池,由任务的多少决定
ExecutorService executorService = Executors.newCachedThreadPool();
//1.3创建单线程池
//ExecutorService es = Executors.newSingleThreadExecutor();
//1.4创建调度线程池
//Executors.newScheduledThreadPool(4);
//2.创建任务
Runnable runnable = new Runnable() {
private int ticket = 100;
@Override
public void run() {
while (true){
if(ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName()+"卖出了,第"+ticket+"张票");
ticket--;
}
}
};
//3.提交任务(提交多次,分配多个线程)
for (int i = 0; i < 4; i++) {
executorService.submit(runnable);
}
//4.关闭线程池
executorService.shutdown();//等提交的任务全部执行完,关闭线程池,但不接受新任务。
//executorService.shutdownNow();//立即停止,不等待
}
}
Callable接口
线程创建的三种方式:
1. 通过继承Thread,重写run方法
2. 实现Runnable接口
3. 实现Callable接口
- jdk5加入,与Runnable接口类似,实现之后代表一个线程任务
- Callable具有泛型返回值、可以声明异常。
public V call() throws Exception;
}
Callable的基本使用
注意:需要将Callable对象转换成可执行任务FutureTask(FutureTask 实现了Runnable接口),再传给线程
package com.wlw.thread.threaddemo02;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* 演示Callable接口的使用
* Callable接口与Runnable接口的区别:
* (1)Callable接口中call方法具有泛型返回值,Runnable接口中的run方法没有返回值
* (2)Callable接口中call方法有声明异常,Runnable接口中的run方法没有声明异常
*/
public class CallableDemo {
public static void main(String[] args) throws Exception {
//功能需求:使用Callable实现1~100的和
//1.创建Callable对象
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"开始计算:");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum = sum + i;
}
return sum;
}
};
//2.将Callable对象转换成可执行任务(FutureTask 实现了Runnable接口)
FutureTask<Integer> task = new FutureTask<>(callable);
//3.创建线程对象,将任务传给线程
Thread thread = new Thread(task);
//4.启动线程
thread.start();
//5.获取结果(get() 方法要等call() 方法执行完毕,才能返回)
Integer integer = task.get();
System.out.println(integer);
}
}
/*
执行结果:
Thread-0开始计算:
5050
*/
Callable结合线程池的使用
注意:Callable对象是可以直接传给线程池的(不需要转换成FutureTask)
线程池.submit (线程池的提交)有返回值,为future : 表示将要执行完任务的结果
package com.wlw.thread.threaddemo02;import java.util.concurrent.*;
/**
* Callable结合线程池的使用
* Callable对象是可以直接传给线程池的(不需要转换成FutureTask)
* 实现计算1~100的和
*/
public class ThreadPool_Callable_Demo {
public static void main(String[] args) throws Exception {
//1.创建线程池
ExecutorService es = Executors.newFixedThreadPool(1);
//2.提交任务 es.submit 的返回值为future : 表示将要执行完任务的结果
Future<Integer> future = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName() + "开始计算");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum = sum + i;
}
return sum;
}
});
//3.获取结果
System.out.println(future.get());
//4.关闭线程池
es.shutdown();
}
}
Future接口
需求:使用两个线程,并发计算150,51100的和,再进行汇总
package com.wlw.thread.threaddemo02;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* Future接口的使用
* 需求:使用两个线程,并发计算1~50,51~100的和,再进行汇总
*/
public class FutureDemo {
public static void main(String[] args) throws Exception{
//1.创建线程池
ExecutorService es = Executors.newFixedThreadPool(2);
//2.提交任务
Future<Integer> future1 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"计算开始");
int sum = 0;
for (int i = 0; i <= 50; i++) {
sum += i;
}
System.out.println("1~50 计算完毕");
return sum;
}
});
Future<Integer> future2 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"计算开始");
int sum = 0;
for (int i = 51; i <= 100; i++) {
sum += i;
}
System.out.println("51~100 计算完毕");
return sum;
}
});
//3.获取结果(get() 方法要等call() 方法执行完毕,才能返回)
System.out.println(future1.get()+future2.get());
//4.关闭线程池
es.shutdown();
}
}
/*
执行结果:
pool-1-thread-1计算开始
pool-1-thread-2计算开始
51~100 计算完毕
1~50 计算完毕
5050
*/
线程同步和异步
线程同步
- 形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续
线程异步
- 形容一次方法调用,异步一旦开始,像是一次消息传递,调用者告知之后立刻返回。
- 二者竞争时间片,并发执行
Lock接口(与synchronized)
- void lock() //获取锁,如锁被占用,则等待。
- boolean tryLock() //尝 试获取锁(成功返回true。失败返回false,不阻塞)
- void unlock() //释放锁
Lock接口的实现类:ReentrantLock
重入锁:ReentrantLock:Lock接口的实现类,与synchronized一样具有互斥锁功能(但比synchronized更强大)
例子1:
package com.wlw.thread.lock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock的使用
* 重入锁
*/
public class MyList {
//创建锁
private Lock lock = new ReentrantLock();
private String[] str = {"A","B","","",""};
int count = 2;
public void add(String value){
try {
lock.lock(); //上锁
str[count] = value;
try {
Thread.sleep(1000);//主动休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
System.out.println(Thread.currentThread().getName()+"存入了"+value);
}finally {
lock.unlock();//解锁
}
}
public String[] getStr() {
return str;
}
}package com.wlw.thread.lock;
import java.util.Arrays;
public class TestList {
public static void main(String[] args) throws Exception {
MyList list = new MyList();
Runnable runnable1 = new Runnable() {
@Override
public void run() {
list.add("Hello");
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
list.add("World");
}
};
//创建线程并启动
Thread t1 = new Thread(runnable1);
Thread t2 = new Thread(runnable2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Arrays.toString(list.getStr()));
}
}
/*
Thread-0存入了Hello
Thread-1存入了World
[A, B, Hello, World, ]
*/
例子2:
package com.wlw.thread.lock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ticket implements Runnable{
private int ticket = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try {
if(ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
ticket--;
} finally {
lock.unlock();
}
}
}
}package com.wlw.thread.lock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestTicket {
public static void main(String[] args) {
//创建实现类对象
Ticket ticket = new Ticket();
//创建线程池
ExecutorService es = Executors.newFixedThreadPool(4);
//提交任务
for (int i = 0; i < 4; i++) {
es.submit(ticket);
}
//关闭线程池
es.shutdown();
}
}
ReadWriteLock接口:ReentrantReadWriteLock
- 一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
- 支持多次分配读锁,使多个读操作可以并发执行。
- 写-写:互斥,阻塞。
- 读-写:互斥,读阻塞写、写阻塞读。
- 读-读:不互斥、不阻塞。
- 在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。
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;
/**
* 读写锁的演示
* ReentrantReadWriteLock
*
*/
public class ReadWriteDemo {
//创建读写锁
private ReentrantReadWriteLock rrl= new ReentrantReadWriteLock();
//获取读锁
private ReadLock readLock = rrl.readLock();
//获取写锁
private WriteLock writeLock = rrl.writeLock();
//重入锁
private Lock lock = new ReentrantLock();
private String value;
public String getValue(){
readLock.lock();//读锁,来上锁
//lock.lock();
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("读取了,"+value);
return this.value;
} finally {
readLock.unlock();
//lock.unlock();
}
}
public void setValue(String value){
writeLock.lock();//写锁,上锁
//lock.lock();
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("写入了,"+value);
this.value = value;
} finally {
writeLock.unlock();
//lock.unlock();
}
}
}package com.wlw.thread.lock;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestReadWrite {
public static void main(String[] args) {
//创建对象
ReadWriteDemo readWriteDemo = new ReadWriteDemo();
//创建线程池
ExecutorService es = Executors.newFixedThreadPool(20);
Runnable read = new Runnable() {
@Override
public void run() {
readWriteDemo.getValue();
}
};
Runnable write = new Runnable() {
@Override
public void run() {
readWriteDemo.setValue("张三"+new Random().nextInt(100));
}
};
long start = System.currentTimeMillis();
//提交2次写
for (int i = 0; i < 2; i++) {
es.submit(write);
}
//提交18次读
for (int i = 0; i < 18; i++) {
es.submit(read);
}
es.shutdown();//关闭
while (!es.isTerminated()){//判断所有线程是否都运行完毕,否则空转
}
long end = System.currentTimeMillis();
System.out.println("用时:"+(end - start));
}
}
/*
读写锁与重入锁 两者用时比较:
用时:3006 vs 用时:20013
*/