Java多线程

本文介绍了Java多线程的基础知识,包括进程、线程和并发原理,并详细讲解了如何通过继承Thread类、实现Runnable和Callable接口创建线程。此外,文章重点探讨了线程池的使用,特别是newFixedThreadPool的线程池关键参数如corePoolSize、maximumPoolSize和RejectedExecutionHandler的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、介绍

进程和线程

1.1 进程

进程是操作系统中运行的一个任务(一个应用程序运行在一个进程中)。是一块包含了某些资源的内存区域。操作系统利用进程将内存划分为不同的内存块。每个程序(进程)独享自己的内存空间。

1.2 线程

线程:进程中包含的一个或多个"执行单元"称为线程。线程共享所属进程的空间。

1.3 并发

并发原理:(不是同时做,而是走走停停,只是间隔时间很小),线程调度机制将CPU时间划分为很多时间片段(时间片),"尽可能均匀分配"给正在运行的程序,获取CPU时间片的线程或进程得以被执行,其他则等待。而CPU则在这些进程或线程上来回切换运行。微观上所有进程和线程是走走停停的,宏观上都在运行,这种都运行的现象叫并发,但是不是绝对意义上的同时发生。

使用场合:
为了宏观上是同时在干多件事时使用。 例如:多线程下载。

并行和并发的区别:
并发:把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行。(单核)
并行:并行是让不同的任务同时在不同的处理器(CPU)上执行,是真的同时执行。例如:多核CPU就是并行执行。

二、创建线程

2.1 继承Thread类

java.lang.Thread类是线程类,其每一个实例表示一个可以并发运行的线程。

//1.创建线程
public class TestThread extends Thread{  //TestThread:线程名
	public void run() {   //需重写run方法,目的是定义该线程要执行的逻辑
	    for(int i=0;i<100;i++){
			System.out.println("我是线程");
	    }
	}
}
//2.实例化线程 :
Thread thread = new TestThread();
//3.启动线程: 
thread.start(); 

此方式的不足:

  • 由于java是单继承,这就导致,一旦继承了Thread就无法再继承其他父类。
  • 由于run方法的方法体被写死了,不利于重用。

注意:
启动线程时调用线程的start()方法而非直接调用run()方法。这样线程才会被纳入线程调度,才会并发运行。
另外,线程启动后,何时运行?运行多久?都是由系统的线程调度来完成,无法人为控制。

2.2 实现Runnable接口

实现java.lang.Runnable接口并重写run方法。特点是:无返回值,无法抛出异常。

在创建线程的时候将Runnable的实例传入并启动线程。从而达到:线程只负责并发运行,任务分离,给什么任务就运行什么任务。

//1.实现Runnable接口,定义"任务"
public class TestRunnable implements Runnable{
	public void run() {
	    for(int i=0;i<100;i++){
			System.out.println("我是线程");
	    }
	}
}
//2.实例化Runnable
Runnable runnable = new TestRunnable();
//3.实例化线程并传入“任务”
Thread thread = new Thread( runnable ); 
//4.启动线程:
thread.start();

2.3 实现Callable接口

实现java.util.concurrent.Callable接口,重写call方法,特点是:有返回值(泛型),可抛出异常。

//1.实现Callable接口
public class CallableTest implements Callable<String>{
	@Override
	public String call() throws Exception {
		//测试返回线程自己的名字
		String name = Thread.currentThread().getName();
		return name;
	}
}
//2.实例化Callable
Callable callable = new CallableTest();
//3.创建FutureTask实例,为的是后期接收返回值
FutureTask<String> task = new FutureTask<String>(callable);
//4.启动线程
Thread thread = new Thread(task);
thread.start();
//5.获取返回值
String str = task.get();
System.out.println(str); 

注意:
前面的三种方式,都可以使用匿名内部类来简化。

2.4 线程池

线程池是为了解决线程重用和控制线程数量。

线程池的概念:首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。以此,来节约创建线程和销毁的资源消耗。

线程池有以下几种实现策略:

  • Executors.newFixedThreadPool(int nThreads) 常用
    创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。
  • Executors.newCachedThreadPool()
    创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
  • Executors.newScheduledThreadPool(int corePoolSize)
    创建一个线程池,它可安排在给定延迟后运行或者定期地执行。
  • Executors.newSingleThreadExecutor()
    创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

2.4.1 newFixedThreadPool

//1.创建具有30个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(30);   
//2.创建Runable
Runnable r1 = new Runable(){
     public void run(){
		//线程体
     }
};
//3.将任务交给线程池,其会分配空闲线程来运行这个任务。无需我们自己创建线程和启动线程。
threadPool.execute(r1); 

//其他方法:
threadPool.submit(r1);    //也可以执行
threadPool.shutdown();    //停止线程池(所有活干完,自动停止线程)
threadPool.shutdownNow(); //直接终止线程,不管任务做完没有

线程池关键参数说明

关于几个线程池的几个常见属性:

  • corePoolSize
    线程池的核心池的大小,即在没有任务需要执行的时候线程池的大小。[保证最小工人数]
    注意:
    ① 如果池中的实际线程数小于corePoolSize,无论是否其中有空闲的线程,都会给新的任务产生新的线程
    ② 如果池中的线程数>corePoolSize and <maximumPoolSize,而又有空闲线程,就给新任务使用空闲线程,如没有空闲线程,则产生新线程。
    ③ 如果池中的线程数=maximumPoolSize,则有空闲线程使用空闲线程,否则新任务放入workQueue。[线程的空闲只有在workQueue中不再有任务时才成立]

  • maximumPoolSize
    线程池允许的最大线程数。

  • workQueue
    工作队列,即:任务队列,添加任务被放进任务队列依次执行。默认大小是:Integer.MAX_VALUE

  • RejectedExecutionHandler
    当线程池的任务缓存队列已满,并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
    ① ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    ② ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
    ③ ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ④ ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值