一 .多线程
- 继承 Thread 类
- 实现 Runnable 接口
- 实现 Callable 接口
- 线程池
重写run方法,创建对象,调用start()方法启动线程
1,新生状态
– 用new关键字建立一个线程后,该线程对象就处于新生状态。
– 处于新生状态的线程有自己的内存空间,通过调用start()方法进入就绪状态。
2,就绪状态
– 处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分
配CPU。
– 当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称为 CPU “ 调 度”。
3,运行状态
– 在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞戒完成任何而死亡。
– 如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。
4,阻塞状态
– 处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,戒等待I/O设备等资源,将让
出CPU并暂时停止自己运行,进入阻塞状态。
– 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,戒等
待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从
原来停止的位置开始继续执行。
5,死亡状态
– 死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个,
一个是正常运行的线程完成了它的全部工作;
另一个是线程被强制性地终止,如通过stop方法来终止一个线程【不推荐使用】;
三是线程抛出未捕获的异常。
6,线程操作的相关方法
7,阻塞状态(sleep/yield/join方法)
1,有三种方法可以暂停Thread执行:
(1),sleep:
不会释放锁,Sleep时别的线程也不可以访问锁定对象。
(2),yield:
让出CPU的使用权,从运行态直接迚入就绪态。让CPU重新挑选哪一个线程进入运行状态。
(3),join:
当某个线程等待另一个线程执行结束后,才继续执行时,使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行
一、继承 Thread 类
- 创建 MyThread 类,让其继承 Thread 类并重写 run() 方法。
- 创建 MyThread 类的实例对象,即创建一个新线程。
- 调用 start() 方法,启动线程。
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("我是通过继承 Thread 类创建的多线程,我叫" + Thread.currentThread().getName());
}
}
class TestMyThread {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.setName("Thread-1");
MyThread myThread2 = new MyThread();
myThread2.setName("Thread-2");
MyThread myThread3 = new MyThread();
myThread3.setName("Thread-3");
myThread1.start();
myThread2.start();
myThread3.start();
}
}
线程的执行顺序和代码中编写的顺序没有关系,线程的执行顺序是具有随机性的。
二、实现 Runnable 接口
Runnable 接口只有一个 run() 方法,源码如下:
public interface Runnable {
public abstract void run();
}
- 创建 MyRunnable 类实现 Runnable 接口。
- 创建 MyRunnable 类的实例对象 myRunnable 。
- 把实例对象 myRunnable 作为参数来创建 Thread 类的实例对象 thread,实例对象 thread 就是一个新线程。
- 调用 start() 方法,启动线程。
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我是通过实现 Runnable 接口创建的多线程,我叫" + Thread.currentThread().getName());
}
}
class TestMyRunnable {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
相比于继承 Thread 类的方法来说,实现 Runnable 接口是一个更好地选择,因为 Java 不支持多继承,但是可以实现多个接口。
有一点值得注意的是 Thread 类也实现了 Runnable 接口,这意味着构造函数 Thread(Runnable target) 不仅可以传入 Runnable 接口的对象,而且可以传入一个 Thread 类的对象,这样就可以将一个 Thread 对象中的 run() 方法交由其他线程进行调用。
三、线程池
Java通过Executors创建线程池,分别为:
1.Executors.newCachedThreadPool()
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程, 适用于服务器负载较轻,执行很多短期异步任务
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
2,Executors.newFixedThreadPool(3)
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
线程池核心的参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
- corePoolSize 线程池核心线程大小
- maximumPoolSize 线程池最大线程数量
- keepAliveTime空闲线程存活时间
- unit 空闲线程存活时间单位
- workQueue 工作队列
- threadFactory 线程工厂
- handler 拒绝策略
线程池四种拒绝任务策略
1、直接丢弃(DiscardPolicy)
2、丢弃队列中最早的任务(DiscardOldestPolicy)。
3、抛异常(AbortPolicy)
4、将任务分给调用线程来执行(CallerRunsPolicy)。
应用举例:
一个火车站购票系统,有100张车票,由多个窗口售卖,直到卖完为止,这时程序中,需要使用到多线程,如果不对其进行限制的话,就会出现同一个座位卖两张票的情况。
单线程:简单来说,就是一个窗口售卖100张票
多线程:多个窗口,同事售卖100张票。
二.单例双重锁
描述
为了保证线程的安全性,往往要以牺牲性能为代价。为了兼得二者,前人进行了多番尝试,也确实创造出诸多有效方案,双重检查锁就是其中的一种。
DCL:Double Check Lock(双重检查锁)。令人哭笑不得的是,其闻名原因不是因为有效性,而是行业标杆级的错误性。双重检查锁同时体现了同步中的独占性与可见性同等的重要性,因此成为多线程学习中必学的经典案例。
为什么需要第二次检查?
首先我们来分析没有第二次检查的情况:当userBean为null时,两个线程可以并发地进入if语句内部。 然后,一个线程进入synchronized块来初始化userBean,而另一个线程则被阻断。当第一个线程退出synchronized块时,等待着的线程进入并创建另一个DBHelper对象。当第二个线程进入 synchronized 块时,它并没有检查 instance 是否非 null。因此我们需要对instance进行第二次非空检查,这也是“双重检查加锁”名称的由来。
例子
public class UserBean {
private static UserBean userBean=null;
private UserBean() {
}
//线程锁
public static UserBean getInstance(){
/** 采用双重检查加锁实例化单件*/
//第一次检查
if (Objects.isNull(userBean)){
synchronized (UserBean.class){
//第二次检查
if (Objects.isNull(userBean)){
userBean=new UserBean();
}
}
}
return userBean;
}
}