并发:交替运行
并行:一起运行
多线程实现方式
继承Thread类
①自己定义一个类继承Thread
public class MyThread extends Thread{
public void run(){
}
}
②重写run方法
public class MyThread extends Thread{
public void run(){
"重写的内容"
}
}
③创建子类对象,并启动线程
MyThread t1 = new MyThread();
t1.setName("线程1的名字");
t1.start("开始运行线程1");
实现Runnable接口
①自己定义一个类实现Runnable接口
public class MyRun implements Runnable{
public void run(){
}
}
②重写里面的run方法
public class MyRun implements Runnable{
public void run(){
"重写的代码"
}
}
注意:在重写run的过程中是不能调用getName这个方法的,因为使用的是自己创建的类,如果要使用的话,可以用Thread t = Thread.currentThread();就相当于返回的是相应线程的对象
③创建自己的类的对象
Myrun mr = new Myrun();
④创建一个Thread类的对象
Thread t1 = new Thread(mr);
利用Callable接口和Future接口(可以获取到多线程运行的结果)
①创建一个类MyCallable实现Callable接口
public class MyCallable implements Callable<V>{
public V call(){
return V;
}
}
②重写call(有返回值)
③创建MyCallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
④创建FutureTask的对象(作用管理多线程运行的结果)
//管理Callanle
FutureTask<V> ft = new FutureTask<>(mc);
⑤创建Thread类的对象,并启动
Thread t1 = new Thread(ft);
t1.start();
V result = ft.get();//获取结果
常见的成员方法
setName()
注意:
- 如果没有给线程设置名字,线程也是有默认名字的
- 如果我们要给线程设置名字,可以用set方法进行设置,也可以用构造方法设置
currentsThread()
- 当JVM虚拟机启动之后,会自动的启动多个线程,其中有一条叫做main线程,它的作用就是去调用main方法,并执行里面的代码
sleep()
- 哪条线程执行到这个方法,那么那条线程就要在这停留相应的时间
- 方法的参数:就表示这个停留的时间,单位毫秒
- 当时间到了之后,线程就会自动醒来,继续执行下面的代码
setPriority(); -----设置优先级
线程的调度(抢占CPU)
抢占式调度:随机性
非抢占式调度
setDeamon() ----- 设置守护线程
当其他线程都结束之后,守护线程也随之结束
例子:如果在聊条过程中,一边发送信息,一边发送文件,当关闭了发送信息的窗口的时候,那么文件也没必要发送,就结束发送文件这个线程
yield() ------ 礼让逻辑
作用,尽可能让各个线程执行的次数均匀
join() ------- 加入线程
线程的生命周期
线程的安全
同步代码块
容易出现问题的原因:在线程执行的时候,有随机性
解决:如果能把线程操作共享数据的代码锁起来,只让一条线程执行,这条线程执行完之后才能让下一条线程执行代码
关键字:synchronized(锁对象){操作共享的数据}
public class MyThread extends Thread{
//static保证锁对象是唯一的
//创建一个任意的锁对象
static Object obj = new Object();
synchronized(obj){
操作代码
}
}
特点:
- 锁默认打开,有一个线程进去了,锁自动关闭
- 里面的代码全部执行完毕,线程出来,锁自动打开
同步方法
特点:
- 同步方法是锁定方法里面的所有代码
- 锁对象不能自己指定
//Ctrl + Alt + m 将代码块抽取成方法
Lock锁
可以手动上锁和释放锁
lock();上锁
unlock();释放锁
死锁(错误)
不要让俩个锁嵌套使用
生产者和消费者(等待唤醒机制)
消费者等待
首先是消费者抢到了CPU的执行权
消费者
- 判断是否生产者生产了数据
- 如果没有就等待(wait)
生产者
- 生产数据
- 制作数据
- 叫醒消费者消费数据(notify)
生产者等待
俩次都是生产者抢到了CPU的执行权
生产者
- 判断是否现在是否生成了数据
- 有:等待
- 没有:生产数据
- 把数据放着
- 叫消费者消费数据
消费者
- 判断是否有数据可以消费
- 没有就等待
- 有就开始消费
- 没有就叫生产者开始生产
常用方法
解决方案
阻塞队列方式实现
生产者和消费者一定要使用一个阻塞队列
take()----获取
put()-----放入
注意:这俩个方法的底层都是上了锁的,不用再格外的去加锁
代码表示
厨师(生产者)
package com.FC;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String> queue){
this.queue = queue;
}
public void run(){
while(true){
try {
queue.put("面条");
System.out.println("厨师放了一碗面条");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
吃货(消费者)
package com.FC;
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie extends Thread{
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String> queue){
this.queue = queue;
}
public void run(){
while(true){
try {
String food = queue.take();
System.out.println("我吃了一碗面条");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
运行代码
package com.FC;
import java.util.concurrent.ArrayBlockingQueue;
public class ThreadDemo {
public static void main(String[] args) {
//创建阻塞队列的对象
ArrayBlockingQueue<String > queue = new ArrayBlockingQueue<>(1);
//创建线程
Cook c = new Cook(queue);
Foodie f = new Foodie(queue);
//给线程设置名字
c.setName("厨师");
f.setName("吃货");
//开启线程
c.start();
f.start();
}
}
线程的状态
线程池
当有任务出现的时候,如果线程池里面为空,就创建一个新的线程,用于执行这个任务,执行完之后,把这个线程放到线程池里面,当下一个任务出现的时候,就不用创建新的线程了,直接从线程池里面拿现成的线程执行任务
用工具类实现
1.创建一个池子
//没有上限的线程池
public static ExecutorService newCachedThreadPool();
//有上限的线程池
public static ExecutorService newFixedThreadPool(int nThreads);
ExecutorService pool1 = Executors.newCachedThreadPool();
2.提交任务
pool1.submit(new MyRunnable());
3.所有的任务全部执行完毕之后,关闭线程池
pool1.shutdown();
自定义实现
自定义线程池中有核心线程和临时线程(都有一定的数量),当有很多任务要进行的时候,核心线程先进行一部分,然后一些任务排在队列里面(有一定的长度),当队列排满了之后,才会创建临时线程。
如果核心线程加上临时线程再加上队伍的长度都小于要执行的任务,那么默认会采用抛弃策略
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//核心线程数,不能小于0
6,//最大线程数,不能小于0,且要大于等于核心线程数
60,//空闲线程最大存活时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(3),//任务队列
Executors.defaultThreadFactory(),//创建线程工厂
new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略
);
其他
获取电脑的可用的处理器数目
int count = Runtime.getRuntime().availableProcessors();