JAVA:【基础四】多线程

目录

一、声明多线程的方式

二、线程的五种状态

三、线程的同步方法【交互方法、通信方式】

四、线程池的四种类型

五、线程池的七大参数

六、线程池的流程机制

七、Volatile关键字的理解


一、声明多线程的方式

1.实现Runnable接口实现run方法,通过在父线程中调用start方法来使线程进入到可运行状态

//实现Runnable接口
public class RunnableTest {
  
    public static void main(String[] args) {
        //设置线程名字
        Thread.currentThread().setName("main thread:");
        Thread thread = new Thread(new MyRunnable());
        thread.setName("子线程:");
        //开启线程
        thread.start();
        for(int i = 0; i <5;i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}
  
class MyRunnable implements Runnable {
  
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

2.继承Thread类重写run方法,其实质也是实现Runnable接口,也是通过在父线程中调用start方法来是线程进入可运行状态

//继承Thread类来创建线程
public class ThreadTest {
 
    public static void main(String[] args) {
        //设置线程名字
        Thread.currentThread().setName("main thread");
        MyThread myThread = new MyThread();
        myThread.setName("子线程:");
        //开启线程
        myThread.start();
        for(int i = 0;i<5;i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}
 
class MyThread extends Thread{
    //重写run()方法
    public void run(){
        for(int i = 0;i < 10; i++){
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

3.实现Callable接口实现call()方法,该call()方法将作为线程执行体,并且有返回值,可以声明抛出异常。注意:在调用时,需要使用FutureTask来包装接收结果,并使用get()获取结果

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//实现Callable接口
public class CallableTest {
 
    public static void main(String[] args) {
        //执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
        new Thread(futureTask).start();
        //接收线程运算后的结果
        try {
            Integer sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
 
class MyCallable implements Callable<Integer> {
 
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum;
    }
}

二、线程的五种状态

Java语言定义了5种线程状态,在任意一个时间点,一个线程只能有且只有其中一个状态。,这5种状态如下:

(1)新建(New):使用new创建了线程后,尚未调用start方法时,线程处于这种状态

(2)可运行状态(Runable):调用了start方法后,Runable包括了操作系统线程状态的Running和Ready,也就是处于此状态的线程并不是立即执行,而是等待操作系统来调度,有可能正在执行,也有可能正在等待着CPU为它分配执行时间。

(3)等待(Wating):处于这种状态的线程不会被分配CPU执行时间。等待状态又分为无限期等待有限期等待处于无限期等待的线程需要被其他线程显示地唤醒,没有设置Timeout参数的Object.wait()、没有设置Timeout参数的Thread.join()方法都会使线程进入无限期等待状态;有限期等待状态无须等待被其他线程显示地唤醒,在一定时间之后它们会由系统自动唤醒,Thread.sleep()、设置了Timeout参数的Object.wait()、设置了Timeout参数的Thread.join()方法都会使线程进入有限期等待状态。

(4)阻塞(Blocked):线程被阻塞了,如遇到了synchronized关键字。“阻塞状态”与”等待状态“的区别是:”阻塞状态“在等待着获取到一个排他锁,这个时间将在另外一个线程放弃这个锁的时候发生;而”等待状态“则是在等待一段时间或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态。

(5)结束(Terminated):已终止线程的线程状态,线程已经结束执行

三、线程的同步方法【交互方法、通信方式】

线程有4中同步方法,分别为wait()、sleep()、notify()和notifyAll()。

  • wait():使线程处于一种等待状态,释放所持有的对象锁。
  • sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用它时要捕获InterruptedException异常,不释放对象锁。
  • notify():唤醒一个正在等待状态的线程。注意调用此方法时,并不能确切知道唤醒的是哪一个等待状态的线程,是由JVM来决定唤醒哪个线程,不是由线程优先级决定的。
  • notifyAll():唤醒所有等待状态的线程,注意并不是给所有唤醒线程一个对象锁,而是让它们竞争。

四、线程池的四种类型

1、newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

这种类型的线程池特点是:

工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。

如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统OOM。

2、newFixedThreadPool

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

3、newSingleThreadExecutor

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

4、newScheduleThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

5、newSingleThreadScheduledExecutor

创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行并且可定时或者延迟执行线程活动

五、线程池的七大参数

(1)corePoolSize:线程池中的常驻核心线程数。

(2)maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值大于等于1。

(3)keepAliveTime:多余的空闲线程存活时间,当空间时间达到keepAliveTime值时,多余的线程会被销毁直到只剩下corePoolSize个线程为止。

(4)unit:keepAliveTime的单位。

(5)workQueue:任务队列,被提交但尚未被执行的任务。

(6)threadFactory:表示生成线程池中工作线程的线程工厂,用户创建新线程,一般用默认即可。

(7)handler:拒绝策略,表示当线程队列满了并且工作线程大于等于线程池的最大显示数(maxnumPoolSize)时如何来拒绝请求执行的runnable的策略。

一:corePoolSize 详细描述

(1)在创建了线程池后,当有请求任务来之后,就会安排池中的线程去执行请求任务,近视理解为今日当值线程。
(2)当线程池中的线程数目达到corePoolSize后,就会把到达的任务放入到缓存队列当中。

二:最大线程数(maximumPoolSize)详细描述

该参数定义了一个线程池中最多能容纳多少个线程。当一个任务提交到线程池中时,如果线程数量达到了核心线程数,并且任务队列已满,不能再向任务队列中添加任务时,这时会检查任务是否达到了最大线程数,如果未达到,则创建新线程,执行任务,否则,执行拒绝策略。可以通过源码来看一下。如下:可以看出,当调用submit(Runnable task)方法,将任务提交到线程池中时,会调用execute()方法去执行任务,在该方法内,会进行核心线程数,任务队列的判断,最后决定是执行或者是拒绝。总结起来就是:最大线程数参数,是在已经达到核心线程池参数,并且任务队列已经满的情况下,才去判断该参数。

三:keepAliveTime 详细描述

只有当线程池中的线程数大于corePoolSize时keepAliveTime才会起作用,直到线程中的线程数不大于corepoolSIze。

四:系统默认的拒绝策略有以下几种

  • AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
  • DiscardPolicy:直接抛弃不处理。
  • DiscardOldestPolicy:丢弃队列中最老的任务。
  • CallerRunsPolicy:将任务分配给当前执行execute方法线程来处理。

六、线程池的流程机制

  • 线程池中线程数小于corePoolSize时,新任务将创建一个新线程执行任务,不论此时线程池中存在空闲线程;
  • 线程池中线程数达到corePoolSize时,新任务将被放入workQueue中,等待线程池中任务调度执行;
  • 当workQueue已满,且maximumPoolSize>corePoolSize时,新任务会创建新线程执行任务;
  • 当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理;
  • 当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收该线程;
  • 如果设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收;

七、Volatile关键字的理解

        参考其他文章

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值