并发:交替运行
并行:一起运行
①自己定义一个类继承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接口
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);
①创建一个类MyCallable实现Callable接口
public class MyCallable implements Callable{
public V call(){
return V;
}
}
②重写call(有返回值)
③创建MyCallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
④创建FutureTask的对象(作用管理多线程运行的结果)
//管理Callanle
FutureTask ft = new FutureTask<>(mc);
⑤创建Thread类的对象,并启动
Thread t1 = new Thread(ft);
t1.start();
V result = ft.get();//获取结果
setName()
注意:
currentsThread()
sleep()
setPriority(); -----设置优先级
线程的调度(抢占CPU)
抢占式调度:随机性
非抢占式调度
setDeamon() ----- 设置守护线程
当其他线程都结束之后,守护线程也随之结束
例子:如果在聊条过程中,一边发送信息,一边发送文件,当关闭了发送信息的窗口的时候,那么文件也没必要发送,就结束发送文件这个线程
yield() ------ 礼让逻辑
作用,尽可能让各个线程执行的次数均匀
join() ------- 加入线程
容易出现问题的原因:在线程执行的时候,有随机性
解决:如果能把线程操作共享数据的代码锁起来,只让一条线程执行,这条线程执行完之后才能让下一条线程执行代码
关键字:synchronized(锁对象){操作共享的数据}
public class MyThread extends Thread{
//static保证锁对象是唯一的
//创建一个任意的锁对象
static Object obj = new Object();
synchronized(obj){
操作代码
}
}
特点:
特点:
//Ctrl + Alt + m 将代码块抽取成方法
可以手动上锁和释放锁
lock();上锁
unlock();释放锁
不要让俩个锁嵌套使用
首先是消费者抢到了CPU的执行权
消费者
生产者
俩次都是生产者抢到了CPU的执行权
生产者
消费者
生产者和消费者一定要使用一个阻塞队列
take()----获取
put()-----放入
注意:这俩个方法的底层都是上了锁的,不用再格外的去加锁
package com.FC;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue queue;
public Cook(ArrayBlockingQueue 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 queue;
public Foodie(ArrayBlockingQueue 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 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();