多线程
-
创建线程
- 继承Thread类
- extends Thread
- 重写run()方法
- 调用start启动线程
- 实现Runnable接口
- implements Runnable
- 重写run()方法
- new Thread(new 实现Runnable类()).start();启动线程
-
Thread类底层也是实现的Runnable类,避免单继承的局限性,推荐使用 implement Runnable。
-
线程开启不一定立即执行,执行顺序也由CPU调度。
- 实现Callable类
- 重写call()方法
- 添加返回值类型
- //创建实现Callable的对象 CreateThread3 t1 = new CreateThread3
- //创建执行服务 ExecutorService executorService = Executors.newFixedThreadPool(4);
- //提交执行 executorService.submit(t1);
- //关闭服务 executorService.shutdown();
-
获取线程名称
- Thread.currentThread().getName();
-
线程5种状态
- 新生(NEW)(Thread t = new Thread();线程对象一旦创建就进入了新生状态)
- 就绪(RUNNABLE)(当调用start()方法,线程立马进入就绪状态,但不意味着立即调度执行)
- 运行(RUNNABLE)(通过CPU调度进入运行状态,线程才真正开始执行线程体的代码块)
- 阻塞(TIMED_WAITING)(当调用sleep、wait、同步锁时,线程会进入堵塞状态,不往下执行,待阻塞状态解除,重新进入就绪状态,等待CPU调度)
- 死亡(TERMINATED)(线程中断或结束,一旦进入死亡状态,就不能再次启动)
-
停止线程
- 建议让线程自己停止:利用次数,不建议死循环
- 建议设置一个标志位进行判断是否停止
- 不要使用stop或者destroy等过时的方法和jdk不推荐的方法
-
休眠线程(sleep)
-
礼让线程(yield)
- Thread.yield();
- 线程礼让是让当前线程暂停,但不堵塞,从运行状态重新进入就绪状态,之后CPU再进行调度,礼让不一定会成功!
-
插入线程(join)
- Thread.join();
- 其他线程运行中,join可以使当前线程强制插入执行,其他线程会进入阻塞状态,join线程执行完,其他线程才会接着执行
-
线程优先级(1-10,默认是5,超过1-10范围会报错)
- Java中提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行,但优先级越大并不一定先执行
- 设置线程优先级 thread.setPriority(Thread.MIN_PRIORITY);
- 获得线程优先级 thread.getPriority();
- 先设置优先级别,再调用(10是优先级最高)
- 优先级低只是意味着CPU调度的概率低,并不是低就不会被调用
-
守护线程
- 线程分为用户线程和守护线程,默认都是用户线程,需要setDaemon(true);
- 虚拟机必须确保用户线程能够执行完毕
- 虚拟机不用等待守护线程执行完毕(监控内存,gc垃圾回收机制等等,守护线程不用管)
- 虽然守护线程没有停止方法,但是它会判断用户线程执行完毕后,守护线程也会自动停止
-
synchronized(同步方法或者同步块)
- 用来保证线程安全,锁的是this对象
- synchronized (Object){//todo}
-
锁的对象是变化的量,需要增删改的对象
- 同步监视器,锁定要修改的对象
- 第一个线程进来,锁定同步监视器,执行其中代码
- 第二个线程访问,发现被锁定,无法访问,只能等待
- 第一个线程执行完,解锁同步监视器
- 第二个线程发现没有锁,然后自己再锁定并访问
- 缺陷:如果将一个比较大的方法声明为synchronized,将会影响效率
-
产生死锁的四个必要条件
- 一个资源只能被一个进程使用
- 一个进程因请求资源而阻塞时,对以获得的资源也保持不放
- 进程已获得的资源,在未使用之前,不能强行剥夺
- 多个进程之间形成了一种头尾相接的循环等待资源关系
-
只要想办法破掉其中一个条件或者多个条件,就可以避免死锁
-
Lock
- Lock是显式锁,需要手动开启和关闭,而synchronized是隐式锁,出了作用域会自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 定义Lock锁:private final ReentrantLock reentrantLock = new ReentrantLock();
- 加锁:reentrantLock.lock();
- 解锁:reentrantLock.unlock();一般放到finally方法块中
- 使用Lock锁,jvm将花费较少的时间来调度线程,性能更好,也具有更好的扩展性
-
线程通信
- wait(); 表示线程一直等待,知道其他线程通知,与sleep不同,sleep会带着锁,而wait会释放锁,不占用资源
- wait(毫秒); 可以赋值毫秒,表示线程等待的时长
- notify(); 唤醒一个处于wait等待状态的线程
- notifyAll(); 唤醒同一个对象上所有wait方法的线程,优先级高的线程优先调度
-
线程池
- 背景:经常创建、销毁以及并发下的线程,对性能影响很大
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放入池中,可以避免频繁创建、销毁,实现重复利用
- 好处:
- 提高响应速度,减少了创建新线程的时间
- 降低资源消耗,重复利用线程池中线程,不需要每次都创建
- 便于线程管理
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数量
- keepAliveTime:线程没有任务时最多保持多久时间后会终止
package com.qing.thread.pool;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池
*/
public class ThreadPool {
public static void main(String[] args) {
//创建线程池的大小
//创建服务
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//execute没有返回值
service.execute(new MyThread());
//submit有返回值
service.submit(new MyThread());
service.submit(new MyCallableThread());
//关闭服务
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
class MyCallableThread implements Callable {
@Override
public Boolean call() {
System.out.println("实现Callable类");
return true;
}
}