线程、多线程、线程池

进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位。

线程的状态变化
创建状态

在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时它已经有了相应的内存空间和其他资源,但还处于不可运行状态。新建一个线程对象可采用Thread 类的构造方法来实现,例如 “Thread thread=new Thread()”。

就绪状态

新建线程对象后,调用该线程的 start() 方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待 CPU 服务,这表明它已经具备了运行条件。

运行状态

当就绪状态被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的 run() 方法。run() 方法定义该线程的操作和功能。

阻塞状态

一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作,会让 CPU 暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(),suspend(),wait() 等方法,线程都将进入阻塞状态,发生阻塞时线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。

死亡状态

线程调用stop()方法时或run()方法执行结束后,即处于死亡状态。

实现多线程编程两种方式
继承Thread类
class MyThread extends Thread{  // 继承Thread类,作为线程的实现类
    private String name;	// 表示线程名称
    public MyThread(String name){ // 通过构造方法初始化name
        this.name = name;
    }
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(name + "运行,i=" + i);
        }
    }
}
public class ThreadDemo01{
    public static void main(String arg[]){
        MyTread p1 = new MyTread("线程A");
        MyTread p2 = new MyTread("线程B");  //实例化对象
        p1.start();
        p2.start(); //启动多线程
    }
}
实现Runnable接口
class MyTread1 implements Runnable{  // 实现Runnable接口,作为线程的实现类
    private String name;	// 表示线程名称
    public MyTread1(String name){ // 通过构造方法初始化name
        this.name = name;
    }
    public void run(){
        System.out.println(name + "运行"); // 重写run方法,作为线程的操作主体
    }
}

public class ThreadDemo02 {

    public static void main(String[] args) {

        MyTread1 p1 = new MyTread1("线程A");  // 实例化对象
        MyTread1 p2 = new MyTread1("线程B");  // 实例化对象
        MyTread1 p3 = new MyTread1("线程C");  // 实例化对象


        Thread t1 = new Thread(p1) ;  //实例化Tread对象
        Thread t2 = new Thread(p2) ;  //实例化Tread对象
        Thread t3 = new Thread(p3) ;  //实例化Tread对象

        t1.start();     //  启动多线程
        t2.start();     //  启动多线程
        t3.start();     //  启动多线程


    }
}

Thread.java 类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用 Thread 中的 run() 方法,也就是使线程得到运行,多线程是异步的,线程在代码中启动的顺序不是线程被调用的顺序。

Thread是Runnable的子类**,但在Thread类中并没有完全实现 Runnable 接口中的 run() 方法。在 Thread 类中的 run() 方法调用的是 Runnable 接口中的 run() 方法,也就是说此方法是由 Runnable 子类完成的,所以如果要通过继承 Thread 类实现多线程,则必须覆写 run()。

实际上 Thread 类和 Runnable 接口之间在使用上也是有区别的,如果一个类继承 Thread类,则不适合于多个线程共享资源,而实现了 Runnable 接口,就可以方便的实现资源的共享。

多线程方法
currentThread()

​ currentThread()可以返回当前代码段正在被哪个线程调用。

Thread.currentThread.getname() //得到当前线程名

Thread.currentThread().yield() // 线程礼让

isAlive()

​ isAliv方法 判断当前线程是否处于活动状态

Thread.isAlive()

sleep( )

​ sleep()的作用是指在指定的毫秒数内,让当前‘正在执行的线程’休眠。正在执行的线程指的是this.currentTread()返回的线程。

Thread.sleep()

getId( )

​ getId( ) 的作用是取得线程的唯一标识。

Thread.getId()

线性池
  • 重用线性池中的线程
  • 有效控制线性池的最大并发数,避免大量线程之间因互相争夺资源二导致阻塞现象
  • 能对线程进行简单的管理,并提供定时执行以及指向间隔循环执行等功能
线程池的创建

创建线程->调用Executors中的对应方法,比如Executors.newFixedThreadPool(int nThreads)

ThreadPoolExecutor是我们线程池核心类,首先看看线程池类的主要参数有哪些。

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters and default thread factory.
 */
public ThreadPoolExecutor(int corePoolSize,  // 线程池的核心大小(最小线程池大小)
                          int maximumPoolSize, // 最大线程池大小
                          long keepAliveTime, // 空闲线程存活时间
                          TimeUnit unit, // 销毁时间单位
                          BlockingQueue<Runnable> workQueue, // 存储等待执行线程的工作队列
                          ThreadFactory threadFactory, // 创建线程的工厂
                          RejectedExecutionHandler handler) { // 拒绝策略(当工作队列、线程池全已满时如何拒绝新任务,默认抛出异常。)
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
}

线程池工作流程

  • 如果线程池中的线程小于corePoolSize时就会创建新线程直接执行任务
  • 如果线程池中的线程大于corePoolSize时就会暂时把任务存储到工作队列workQueue中等待执行。
  • 如果工作队列workQueue也满时,当线程数小于最大线程池数maximumPoolSize时就会创建新线程来处理,而线程数大于等于最大线程池数maximumPoolSize时就会执行拒绝策略。
newSingleThreadExecutor

创建一个单线程化的线程池(只有一个线程的线程池)

newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。(创建固定大小的线程池)

ExecutorService es = Executors.newFixedThreadPool(3);
newSheduledThreadPool

创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。 (创建一个可调度的线程池)

newCachedThreadPoo

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。(创建不限线程数上限的线程池,任何提交的任务都会立即执行)

拒绝策略
AbortPolicy

简单粗暴,直接抛出拒绝异常,这也是默认的拒绝策略。

CallerRunsPolicy

如果线程池未关闭,则会在调用者线程中直接执行新任务,这会导致主线程提交线程性能变慢。

DiscardPolicy

从方法看没做任务操作,即表示不处理新任务,即丢弃。

DiscardOldestPolicy

抛弃最老的任务,就是从队列取出最老的任务然后放入新的任务进行执行。

提交线程
es.submit(xxRunnble); // submit返回一个Future对象,如果想知道线程结果就使用submit提交
es.execute(xxRunnble); //execute没有返回值,如果不需要知道线程的结果就使用execute方法,性能会好很多。
关闭线程
es.shutdown();  //不再接受新的任务,之前提交的任务等执行结束再关闭线程池。
es.shutdownNow(); //不再接受新的任务,试图停止池中的任务再关闭线程池,返回所有未处理的线程list列表。
总结

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到多个任务上,而且由于在请求到达时线程已经存在,所以消除线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值