线程
线程
1 线程概念
进程:指正在内存中运行的程序。进程是处于运行过程中的程序,并且具有一定的独立功能。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行。一个进程中至少有一个线程,也可以有多个线程。
在java中线程之间采用的是抢占式调度,优先级高的线程抢到CPU资源的概率更高。如果线程的优先级相同,那么会随机选择一个线程执行。
CPU使用抢占式调度模式在多个线程间进行着高速的切换,对于CPU的一个核而言,某个时刻,只能执行一个线程。而CPU在多个线程切换速度相对我们的感觉要快得多,所以感觉上是同时在进行。
实际上多线程并不能提高程序的运行速度,但是能够提高CPU的利用率。
多线程的好处:
- 提高用户体验
- 提高CPU利用率
线程在执行的过程中会和计算机硬件进行交互,线程在和计算机硬件交互的时候会暂时空置CPU。
2 主线程
当jvm启动后,必然有一个执行路径(线程)从main方法开始,一直执行到main方法结束。这个线程在java中称之为主线程(main线程)
public static void main(String[] args) {
// 获取线程名称
Thread thread = Thread.currentThread();
String name = thread.getName();
System.out.println(name);
}
3 创建线程
线程的顶级父类是Thread类
3.1 创建线程方式一
创建线程的步骤
- 定义一个类继承Thread
- 重写run方法
- 创建子类对象
- 调用start方法,开启线程并让线程执行
public class MyThread extends Thread{
@Override
public void run() {
// 编写需要其他线程执行的代码
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + "线程正在执行" + i);
}
}
}
public static void main(String[] args) {
// 创建自定义的线程对象
MyThread myThread = new MyThread();
// 调用start方法,让其他线程执行run方法
myThread.start();
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName()+ "线程正在执行" + i);
}
}
3.2 创建线程方式二[推荐]
- 定义类实现Runnable接口
- 实现接口中的run方法
- 创建Thread类对象
- 将Runnable接口的子类对象作为参数传递给Thread类的构造方法
- 调用Thread类的start方法开启线程
public class MyRunnable implements Runnable{
@Override
public void run() {
// 编写子线程执行的代码
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}
}
public class TestDemo2 {
public static void main(String[] args) {
// 创建接口的实现类对象
Runnable runnable = new MyRunnable();
// 创建线程对象
Thread thread = new Thread(runnable);
// 调用start方法,开启线程
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}
}
public static void main(String[] args) {
// 创建接口的实现类对象
// Runnable runnable = new MyRunnable();
// // 创建线程对象
// Thread thread = new Thread(runnable,"子线程");
// // 调用start方法,开启线程
// thread.start();
// new Thread(new Runnable() {
// // 匿名内部类
// @Override
// public void run() {
// for (int i = 0; i < 100; i++) {
// System.out.println(Thread.currentThread().getName() + "正在执行");
// }
// }
// },"线程1").start();
// lambda表达式
new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}).start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行");
}
}
3.3 创建线程方式三
定义类实现Callable接口
Callable<V> 接口和Runnable接口的区别?
Callable接口有泛型,call方法具有返回值,并且可以抛出异常
Runnable接口没有泛型,run方法无参无返回值,不能抛出异常
// 需求: 模拟多个账户对同一个账户进行存钱(转账)/取钱
// 对张三账户进行操作
// 李四对张三转账 1000
// 王五对张三转账 2000
// 张三妻子进行取钱 1000
实现方式一:
package cn.javasm.demo;
import java.math.BigDecimal;
import java.util.concurrent.Callable;
/**
* @className: AccountDemo
* @description:
* @author: gfs
* @date: 2025/7/15 11:28
* @version: 0.1
* @since: jdk17
*/
public class AccountDemo implements Callable<BigDecimal> {
// 张三的余额
private static BigDecimal balance = new BigDecimal("2000");
// 转账/取钱的金额
private BigDecimal money;
private boolean flag = false; // false 转账 true 取钱
public AccountDemo(BigDecimal money, boolean flag) {
this.money = money;
this.flag = flag;
}
@Override
public BigDecimal call() throws Exception {
if (flag){
if (money.compareTo(balance) > 0){
throw new RuntimeException("余额不足,取钱失败");
}
balance = balance.subtract(money);
System.out.println(Thread.currentThread().getName() + "取钱:" + money);
}else {
System.out.println(Thread.currentThread().getName() + "转账:" + money);
balance = balance.add(money);
}
return balance;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建金额对象
BigDecimal money1 = new BigDecimal("1000");
BigDecimal money2 = new BigDecimal("2000");
// 创建Callable实现类对象
AccountDemo lisiCun = new AccountDemo(money1, false);
AccountDemo wangwuCun = new AccountDemo(money2, false);
AccountDemo wifeQu = new AccountDemo(money1, true);
// Callable接口和Runnable接口没有直接关系
// 可以通过FutureTask建立联系
FutureTask<BigDecimal> task1 = new FutureTask<>(lisiCun);
FutureTask<BigDecimal> task2 = new FutureTask<>(wangwuCun);
FutureTask<BigDecimal> task3 = new FutureTask<>(wifeQu);
Thread lisi = new Thread(task1,"李四");
Thread wangwu = new Thread(task2,"王五");
Thread wife = new Thread(task3,"wife");
// 开启线程
lisi.start();
wangwu.start();
wife.start();
System.out.println("李四转账完成之后,账户余额" + task1.get());
System.out.println("王五转账完成之后,账户余额" + task2.get());
System.out.println("wife取钱完成之后,账户余额" + task3.get());
}
实现方式二:
public class Account {
private static BigDecimal balance = new BigDecimal("2000");
public static BigDecimal cunMoney(BigDecimal money){
if (money == null || money.doubleValue() < 0){
throw new NullPointerException("数据不合法");
}
System.out.println(Thread.currentThread().getName() + "转账" + money);
balance = balance.add(money);
return balance;
}
public static BigDecimal quMoney(BigDecimal money){
if (money == null || money.doubleValue() < 0){
throw new NullPointerException("数据不合法");
}
if (money.compareTo(balance) > 0){
throw new RuntimeException("余额不足,取钱失败");
}
balance = balance.subtract(money);
System.out.println(Thread.currentThread().getName() + "取钱:" + money);
return balance;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建金额对象
BigDecimal money1 = new BigDecimal("1000");
BigDecimal money2 = new BigDecimal("2000");
// Callable接口和Runnable接口没有直接关系
// 可以通过FutureTask建立联系
FutureTask<BigDecimal> task1 = new FutureTask<>(()-> Account.cunMoney(money1));
FutureTask<BigDecimal> task2 = new FutureTask<>(()-> Account.cunMoney(money2));
FutureTask<BigDecimal> task3 = new FutureTask<>(()-> Account.quMoney(money1));
Thread lisi = new Thread(task1,"李四");
Thread wangwu = new Thread(task2,"王五");
Thread wife = new Thread(task3,"wife");
// 开启线程
lisi.start();
wangwu.start();
wife.start();
System.out.println("李四转账完成之后,账户余额" + task1.get());
System.out.println("王五转账完成之后,账户余额" + task2.get());
System.out.println("wife取钱完成之后,账户余额" + task3.get());
}
4 售票案例
课堂练习:
模拟电影院售票窗口,实现3个窗口同时卖"哪吒2"这场电影的票,总共100张票,卖完为止。
实现方式一:
public class Ticket implements Runnable {
// 定义变量,模拟总共的100张票
int ticket = 100;
@Override
public void run() {
// 卖票
while (true) {
if (ticket <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
}
}
}
public static void main(String[] args) {
// 创建票对象
Ticket ticket = new Ticket();
// 创建3个窗口
Thread thread1 = new Thread(ticket,"窗口1");
Thread thread2 = new Thread(ticket,"窗口2");
Thread thread3 = new Thread(ticket,"窗口3");
// 开启线程,卖票
thread1.start();
thread2.start();
thread3.start();
}
实现方式二:
public class TicketDemo {
static int ticket = 100;
public static void saleTicket(){
while (true){
if (ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
}
}
}
public static void main(String[] args) {
// 创建3个窗口
// Thread thread1 = new Thread(()->{
// TicketDemo.saleTicket();
// });
Thread thread1 = new Thread(TicketDemo::saleTicket,"窗口1");
Thread thread2 = new Thread(TicketDemo::saleTicket,"窗口2");
Thread thread3 = new Thread(TicketDemo::saleTicket,"窗口3");
thread1.start();
thread2.start();
thread3.start();
}
当我们多次运行售票案例会发现:
- 出现重复的票
- 出现0的票
- 出现-1的票
如果多个线程在同时运行,而这些线程可能会同时运行某段代码,程序每次运行的结果和单线程一致,而且其他的变量的值和预期也一样,就是线程安全。
售票案例中和预期结果不一样,称之为线程安全隐患。
什么时候会出现线程安全隐患?
- 多线程
- 有共享资源
- 修改操作
上面三个条件只要破坏一个就可以解决线程安全隐患。
5 线程安全隐患处理
5.1 同步代码块
格式:
synchronized(锁对象){
可能会产生线程安全隐患的代码;
}
同步代码块中的代码同一时刻只能有一个线程执行。
同步代码块中的锁对象可以是任意的对象,但是多个线程时,要使用同一个锁对象才能够保证线程安全。
当一条线程抢到锁对象之后,其他线程只能等待这条线程执行完毕后释放锁对象,然后所有线程重新去抢锁对象,抢到锁对象的线程可以执行同步代码块中的内容。
synchronized (obj) {
if (ticket <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
}
5.2 同步方法
在方法上面添加synchronized,整个方法同一时刻只能有一个线程执行。
private synchronized boolean method() {
if (ticket <= 0) {
return true;
}
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
return false;
}
同步方法的锁对象是this
静态同步方法,在静态方法上面添加synchronized,整个静态方法同一时刻只能有一个线程执行
// 静态同步方法
public static synchronized void saleTicket(){
while (true){
if (ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
}
}
静态同步方法的锁对象是 类名.class
5.3 Lock[了解]
已经被同步代码块替代
需要手动加锁和释放锁
Lock lock = new ReentrantLock();
@Override
public void run() {
// 卖票
while (true) {
try {
// 让线程休眠1毫秒
Thread.sleep(1L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 加锁
lock.lock();
try {
if (ticket <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
} finally {
// 释放锁 一定要保证这行代码执行 所有放在finally里面
lock.unlock();
}
}
同步:一段代码在同一时刻只允许一个线程执行
异步:一段代码在同一时刻允许多个线程执行
同步一定是线程安全
线程安全不一定同步
异步不一定线程不安全
线程不安全一定是异步
5.4 死锁
死锁:由于锁的嵌套导致锁之间相互锁死的现象
public class TestDemo5 {
static Printer p = new Printer();
static Scan s = new Scan();
public static void main(String[] args) {
new Thread(()->{
synchronized (p){
p.print();
try {
// 打印一段时间后扫描
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (s){
s.scan();
}
}
}).start();
new Thread(()->{
synchronized (s){
s.scan();
synchronized (p){
p.print();
}
}
}).start();
}
}
// 打印机
class Printer{
public void print(){
System.out.println("打印机在打印...");
}
}
// 扫描仪
class Scan{
public void scan(){
System.out.println("扫描仪在扫描...");
}
}
5.5 ThreadLocal
package cn.javasm.demo;
/**
* @className: TestDemo65
* @description:
* @author: gfs
* @date: 2025/7/14 15:41
* @version: 0.1
* @since: jdk17
*/
public class TestDemo6 {
public static ThreadLocal<Print> threadLocal = new ThreadLocal<>(){
// 匿名内部类 就是ThreadLocal的子类
@Override
protected Print initialValue() {
return new Print();
}
};
public static void main(String[] args) {
// // 从线程上方 下 下方传数据
// // key默认是this
//// threadLocal.set("haha");
//// System.out.println(threadLocal.get());
// String str = "e,吃了么?";
// threadLocal.set(str);
// a();
System.out.println(threadLocal.get());
// Print p = new Print();
new Thread(new BFM()).start();
new Thread(new GFS()).start();
}
public static void a() {
b();
}
private static void b() {
c();
}
private static void c() {
d();
}
private static void d() {
e();
}
private static void e() {
System.out.println(threadLocal.get());
}
}
class Print {
public void print(String str) {
System.out.println("打印机在打印" + str);
}
}
class BFM implements Runnable {
@Override
public void run() {
Print p = TestDemo6.threadLocal.get();
p.print(p + "我是白富美");
p.print(p + "我很有钱");
p.print(p + "我很美");
}
}
class GFS implements Runnable {
@Override
public void run() {
Print p = TestDemo6.threadLocal.get();
p.print(p + "我是高富帅");
p.print(p + "我也很有钱");
p.print(p + "我很帅");
}
}
6 线程通信
6.1 等待唤醒机制
package cn.javasm.demo;
/**
* @className: TestDemo7
* @description:
* @author: gfs
* @date: 2025/7/14 16:12
* @version: 0.1
* @since: jdk17
*/
public class TestDemo7 {
public static void main(String[] args) {
Student student = new Student();
student.setName("周杰伦");
student.setGender("男生");
new Thread(new Ask(student)).start();
new Thread(new Change(student)).start();
}
}
class Student{
/**
* 姓名
*/
private String name;
/**
* 性别
*/
private String gender;
/**
* 规定true表示Ask线程执行
* 规定false表示Change线程执行
*/
public boolean flag = true;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
// 问问题
class Ask implements Runnable{
private Student student;
public Ask(Student student) {
this.student = student;
}
@Override
public void run() {
while (true){
synchronized (student){
if (!student.flag){
try {
// 表示让当前线程陷入等待
student.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("老师,我是" + student.getName() + ",我是" + student.getGender() + ",我要问问题");
student.flag = false;
student.notify();
}
}
}
}
// 切换学生
class Change implements Runnable{
private Student student;
public Change(Student student) {
this.student = student;
}
@Override
public void run() {
while (true){
synchronized (student){
if (student.flag){
try {
student.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
if ("周杰伦".equals(student.getName())){
student.setName("昆凌");
student.setGender("女生");
}else {
student.setName("周杰伦");
student.setGender("男生");
}
student.flag = true;
// 唤醒正在等待的线程
student.notify();
}
}
}
}
6.2 生产者消费者模式
练习:一个线程作为消费者,一个线程作为生产者。生产者每生产一次,消费者就消费一次。生产者每次生产的商品数量以及消费者每次消费的数量用随机数产生即可。每一次生产的商品数量和上一次剩余的商品数量之和不能超过1000.
package cn.javasm.demo;
/**
* @className: TestDemo
* @description:
* @author: gfs
* @date: 2025/7/15 10:34
* @version: 0.1
* @since: jdk17
*/
public class TestDemo {
public static void main(String[] args) {
Product product = new Product();
new Thread(new Producer(product)).start();
new Thread(new Consumer(product)).start();
}
}
/**
* 生产者
*/
class Producer implements Runnable{
private Product product;
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true){
synchronized (product){
if (!product.flag){
try {
// 让线程陷入等待
product.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 计算本次所能生产的最大数量
int max = 1000 - product.getCount();
// 计算本次生产的实际数量
int count = (int)(Math.random() * (max + 1));
// 计算本次所能提供的商品数量
product.setCount(product.getCount() + count);
System.out.println("本次生产的商品数量是:" + count + ",本次提供的商品数量是:" + product.getCount());
product.flag = false;
// 唤醒等待的线程
product.notify();
}
}
}
}
class Consumer implements Runnable{
private Product product;
public Consumer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true){
synchronized (product){
if (product.flag){
try {
product.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 计算本次所消费的商品数量
int count = (int)(Math.random() * (product.getCount() + 1));
// 计算本次剩余的商品数量
product.setCount(product.getCount() - count);
System.out.println("本次消费的数量是:" + count + ",本次剩余的商品数量是:" + product.getCount());
product.flag = true;
product.notify();
}
}
}
}
class Product{
/**
* 商品数量
*/
private int count;
// true代表生产 false代表消费
public boolean flag = true;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
唤醒所有的版本
package cn.javasm.demo;
/**
* @className: TestDemo
* @description:
* @author: gfs
* @date: 2025/7/15 10:34
* @version: 0.1
* @since: jdk17
*/
public class TestDemo {
public static void main(String[] args) {
Product product = new Product();
new Thread(new Producer(product)).start();
new Thread(new Producer(product)).start();
new Thread(new Producer(product)).start();
new Thread(new Consumer(product)).start();
new Thread(new Consumer(product)).start();
new Thread(new Consumer(product)).start();
}
}
/**
* 生产者
*/
class Producer implements Runnable{
private Product product;
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true){
synchronized (product){
while (!product.flag){
try {
// 让线程陷入等待
product.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 计算本次所能生产的最大数量
int max = 1000 - product.getCount();
// 计算本次生产的实际数量
int count = (int)(Math.random() * (max + 1));
// 计算本次所能提供的商品数量
product.setCount(product.getCount() + count);
System.out.println("本次生产的商品数量是:" + count + ",本次提供的商品数量是:" + product.getCount());
product.flag = false;
// 唤醒所有的线程
product.notifyAll();
}
}
}
}
class Consumer implements Runnable{
private Product product;
public Consumer(Product product) {
this.product = product;
}
@Override
public void run() {
while (true){
synchronized (product){
while (product.flag){
try {
product.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 计算本次所消费的商品数量
int count = (int)(Math.random() * (product.getCount() + 1));
// 计算本次剩余的商品数量
product.setCount(product.getCount() - count);
System.out.println("本次消费的数量是:" + count + ",本次剩余的商品数量是:" + product.getCount());
product.flag = true;
// 唤醒所有的线程
product.notifyAll();
}
}
}
}
class Product{
/**
* 商品数量
*/
private int count;
// true代表生产 false代表消费
public boolean flag = true;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
7 线程池
7.1 Executors[了解]
public static void main(String[] args) {
demo4();
}
private static void demo4() {
// 创建可以执行延时任务的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// 延迟5秒执行子线程中的代码
System.out.println("目前时间" + LocalDateTime.now());
scheduledExecutorService.schedule(()-> {
System.out.println("helloworld");
System.out.println("目前时间" + LocalDateTime.now());
},5, TimeUnit.SECONDS);
}
private static void demo3() {
// 获取一个可以伸缩的线程池对象
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 30; i++) {
executorService.submit(()-> System.out.println(Thread.currentThread().getName() + "执行了"));
}
}
private static void demo2() {
// 获取指定数量线程对象的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 20; i++) {
executorService.submit(()-> System.out.println(Thread.currentThread().getName() + "执行了"));
}
}
private static void demo1() {
// 获取只有1个线程实例的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
// 开启子线程执行
executorService.submit(()-> System.out.println(Thread.currentThread().getName() + "执行了..."));
}
// 关闭线程池 等待线程任务执行完毕之后再关闭
executorService.shutdown();
// 立即关闭线程池
// executorService.shutdownNow();
}
7.2 ThreadPoolExecutor
// 参数一: corePoolSize 核心线程数量(初始化数量)
// 参数二: maximumPoolSize 最大线程数量
// 参数三: keepAliveTime 在指定时间内回收线程
// 参数四: unit 时间单位
// 参数五: workQueue 工作队列
//ArrayBlockingQueue 有界队列 限制排队的最大任务量 常用
//LinkedBlockingQueue 无界队列 可以排队任意数量的任务
//PriorityBlockingQueue优先级队列
// 参数六: threadFactory 线程工厂
// 参数七: handler 拒绝策略 去主线程执行
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
100,
10,
TimeUnit.MINUTES,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 20; i++) {
threadPoolExecutor.submit(()-> System.out.println(Thread.currentThread().getName() + "执行了"));
}
7.3 单例模式
在整个项目中只存在唯一的一个实例。
单例模式的实现方式:
饿汉式
class TaskManager{
// 饿汉式
private static TaskManager taskManager = new TaskManager();
// 私有的构造方法,保证外部类不能创建对象
private TaskManager(){
System.out.println("构造方法执行了...");
}
public static TaskManager getTaskManager(){
return taskManager;
}
public static void test(){
System.out.println("test...");
}
}
懒汉式
class TaskMgr{
// 懒汉式
private static TaskMgr taskMgr;
// 私有的构造方法,保证外部类不能创建对象
private TaskMgr(){
System.out.println("构造方法执行了");
}
// 当真正需要获取对象时再去创建
public static TaskMgr getInstance(){
// 懒加载 lazy load
if (taskMgr == null){
taskMgr = new TaskMgr();
}
return taskMgr;
}
public static void test(){
System.out.println("test...");
}
}
饿汉式:降低加载效率,线程安全
懒汉式:对象是在真正使用的时候才进行初始化,节省加载时间。线程不安全
7.4 单例模式封装线程池
package cn.javasm.demo;
import java.util.concurrent.*;
/**
* @className: MyThreadPool
* @description:
* @author: gfs
* @date: 2025/7/15 16:01
* @version: 0.1
* @since: jdk17
*/
public class MyThreadPool {
// 饿汉式
private MyThreadPool(){
}
private static final MyThreadPool MY_THREAD_POOL = new MyThreadPool();
private static final ThreadPoolExecutor POOL_EXECUTOR;
static {
POOL_EXECUTOR = new ThreadPoolExecutor(5,
100,
10,
TimeUnit.MINUTES,
new ArrayBlockingQueue<>(100),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
public static MyThreadPool getInstance(){
return MY_THREAD_POOL;
}
/**
* 执行异步任务
*/
public void task(Runnable runnable){
POOL_EXECUTOR.submit(runnable);
}
/**
* 执行异步任务
*/
public <V> void task(Callable<V> callable){
POOL_EXECUTOR.submit(callable);
}
/**
* 任务执行完毕后完毕线程池
*/
public void release(){
POOL_EXECUTOR.shutdown();
}
/**
* 立即关闭线程池
*/
public void releaseNow(){
POOL_EXECUTOR.shutdownNow();
}
}
8 线程的优先级
线程的优先级分为1-10,默认优先级是5.理论上数字越大,优先级越高,抢占到资源的概率就越大。
public static void main(String[] args) {
System.out.println(Thread.MAX_PRIORITY);
System.out.println(Thread.MIN_PRIORITY);
System.out.println(Thread.NORM_PRIORITY);
Thread thread1 = new Thread(new PDemo(),"线程1");
Thread thread2 = new Thread(new PDemo(),"线程2");
// 设置优先级
thread1.setPriority(1);
thread2.setPriority(10);
thread1.start();
thread2.start();
}
9 守护线程(后台线程)
如果一个线程不是守护线程,那么就是被守护线程。首先线程用于守护被守护线程,如果被守护线程结束,守护线程也随之结束。
public static void main(String[] args) {
Thread thread1 = new Thread(new Solider(),"小兵1");
Thread thread2 = new Thread(new Solider(),"小兵2");
// 设置守护线程
thread1.setDaemon(true);
thread2.setDaemon(true);
// 开启线程
thread1.start();
thread2.start();
for (int i = 10; i > 0; i--) {
System.out.println("BOSS还有" + i + "滴血");
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
10 线程的状态
/**
* 新建 创建好还没有执行的线程
*/
NEW,
/**
* 正在执行的线程
*/
RUNNABLE,
/**
* 阻塞
*/
BLOCKED,
/**
* 去等待池中等待的线程 一直到有线程唤醒为止
*/
WAITING,
/**
* 有时间的等待
*/
TIMED_WAITING,
/**
* 结束,终止
*/
TERMINATED;
public static void main(String[] args) {
Thread thread1 = new Thread(()->{
for (int i = 0; i < 100; i++) {
// System.out.println("");
}
});
thread1.start();
System.out.println(thread1.getState());
}
wait和sleep的区别?
sleep需要指定休眠时间,到点自然醒.如果线程没有锁,那么会释放执行权。如果线程有锁,那么就不释放执行权。这个方法是设计在Thread类上,是一个静态方法。
wait可以指定也可以不指定时间,如果不指定时间需要进行唤醒。释放执行权,也释放锁。这个方法是设计在Object上,是一个普通方法。
注意:等待唤醒机制必须结合锁来使用

浙公网安备 33010602011771号