目录
1.3 多线程的第三种实现方式:Callable接口和Future接口
在之前,已经学习了C++的多线程编程,现在一起来看一下java是怎么实现的吧
Linux之多线程概念&线程控制_linux 线程执行到某一函数时,强制另一个线程启动-CSDN博客
- 继承Thread类的方式进行实现
- 实现Runnable接口的方式进行实现
- 利用Callable接口和Future接口的方式进行实现
多线程第二种实现方式:
- 自己手动定义一个类去实现Runnable接口。
- ?重写里面的run方法。
- 创建自己的类的对象。
- ?创建一个Thread类的对象,并开启线程。
线程第三种实现方式:
- 创建一个类MyCallable实现Callable接口。
- 重写里面的call方法。( 返回值表示多线程运行结果 )
- 创建MyCallable的对象。( 表示多线程要执行的任务 )
- 创建FutureTask的对象。( 作用管理多线程运行的结果 )
- 创建Thread类的对象,并启动线程。( 表示线程 )?
特点:?可以获取到多线程运行的结果。
优点 | 缺点 | |
继承Thread类 | 变成比较简单,可以直接使用Thread类中的方法 | 可以扩展性较差,不能再继承其他的类 |
实现Runnable | 扩展性强,实现该接口的同时还可以继承其他的类 | 编程相对复杂,不能直接使用Thread类中的方法 |
实现Callable接口 |
方法名称 | 说明 |
String?getName?( ) | 返回此线程的名称 |
void?setName?( String name ) | 设置线程的名字(构造方法也可以设置名字) |
static?Thread?currentThread?( ) | 获取当前线程的对象 |
static?void?sleep?( long time ) | 让线程休眠指定的时间,单位为毫秒 |
setPriority?(int newPriority ) | 设置线程的优先级 |
final int?getPriority?( ) | 获取线程的优先级 |
final void?setDaemon?( boolean on ) | 设置为守护线程 |
public static void?yield?( ) | 出让线程 / 礼让线程 |
public static void?join?( ) | 插入线程 / 插队线程 |
public class ThreadDeom {
public static void main(String[] args) throws InterruptedException {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
//在不给线程设置名字的时候,线程也是有默认的名字的
//System.out.println(mt1.getName());
mt1.setName("火车");
mt2.setName("飞机");
//mt1.start();
//mt2.start();
//获取的就是main线程的名字
System.out.println(Thread.currentThread().getName());
System.out.println("hhhhhhhhhhhhhh");
Thread.sleep(1000);//按Alt + Enter直接选择抛出异常
System.out.println("hhhhhhhhhhhhhh");
}
}
System.out.println(mt1.getPriority());
System.out.println(mt2.getPriority());
mt1.setPriority(10);
mt2.setPriority(1);
final void?setDaemon?( boolean on )
Java的线程不安全和C++的是一样的,都是因为多个线程同时访问同一个临界资源
解决办法也是引入锁,Java的锁是synchronized
synchroize(){
}
就是把synchronized关键字加到方法上
- 特点1? :?同步方法是锁住方法里面的所有代码
- 特点2 :?锁对象不能自己指定。?
这个和C++的使用方法几乎是一样的
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作。
Lock中提供了获得锁和释放锁的方法:
成员方法 | 说明 |
void?lock?( ) | 获得锁 |
void?unlock?( ) | 释放锁 |
Lock是接口不能直接实例化,这里采用它的实现类 ReentrantLock 实例化。
构造方法 | 说明 |
ReentrantLock ( ) | 创建一个? ReentrantLock 的实例 |
java的死锁和C++一样的原因
Linux 多线程安全之----死锁问题_linux 多个 mutex 锁住一个资源出现死锁-CSDN博客
常见方法:
成员方法 | 说明 |
void?wait?( )? | 当前线程等待,直到被其他线程唤醒 |
void?notify?( ) | 所及唤醒单个线程 |
void?notifyAll?( ) | 唤醒所有线程 |
例子:
生产者代码
//Desk.java
public class Desk {
// 作用: 控制生产者和消费者的执行
//判断桌子上是否有面条: 0:没有 ; 1:有
public static int foodFlag = 0;
//定义总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
//Foodie.java
public class Foodie extends Thread {
@Override
public void run() {
// 1.循环
while (true) {
// 同步代码块
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
// 先判断桌子上是否有面条
if (Desk.foodFlag == 0) {
// 没有:等待
try {
Desk.lock.wait(); // 让当前线程与锁进行绑定
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 把吃的总数- 1
Desk.count--;
// 有: 开吃
System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗");
// 吃完之后:唤醒厨师继续做
Desk.lock.notifyAll();
// 修改桌子的状态
Desk.foodFlag = 0;
}
}
}
}
}
}
消费者代码
//Desk.java
public class Desk {
// 作用: 控制生产者和消费者的执行
//判断桌子上是否有面条: 0:没有 ; 1:有
public static int foodFlag = 0;
//定义总个数
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
//Foodie.java
public class Foodie extends Thread {
@Override
public void run() {
// 1.循环
while (true) {
// 同步代码块
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
// 先判断桌子上是否有面条
if (Desk.foodFlag == 0) {
// 没有:等待
try {
Desk.lock.wait(); // 让当前线程与锁进行绑定
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 把吃的总数- 1
Desk.count--;
// 有: 开吃
System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗");
// 吃完之后:唤醒厨师继续做
Desk.lock.notifyAll();
// 修改桌子的状态
Desk.foodFlag = 0;
}
}
}
}
}
}
之前多线程编程的弊端还是挺大的:
弊端一:用到线程的时候就要创建 | 弊端二:用完之后线程消失 |
因此,我们我们引入线程池:
线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。
- 创建一个池子,池子中是空的。
- 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子;下次再次提交任务时,不需要创建新的的线程,直接复用已有的线程即可。
- 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待。
Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。
方法名称 | 说明 |
public static ExecutorService?newCachedThreadPool?( ) | 创建一个没有上限的线程池 |
public static ExecutorService?newFixedThreadPool?( int nThreads ) | 创建有上限的线程池 |
- 核心元素一:核心线程的数量(不能小于0)
- 核心元素二:线程池中最大线程的数量(最大数量>=核心线程数量)
- 核心元素三:空闲时间(值)(不能小于0)
- 核心元素四:空闲时间(单位)(用TimeUnit指定)
- 核心元素五:堵塞队列(不能为null)
- 核心元素六:创建线程的方式(不能为null)
- 核心元素七:要执行的任务过多时的解决方案(不能为null)
不断的提交任务,会有以下三个临界点:
6.3 最大并行数
CPU密集型运算 (读取文件操作比较少) | |
I/O密集型运算 (读取文件操作比较多) |
查看最大并行数
public class MyThreadPoolDemo {
public static void main(String[] args) {
//向Java虚拟机返回可用处理器的数目
int count = Runtime.getRuntime().availableProcessors();
System.out.println(count); //12
}
}