java中线程的四种创建方式

先来了解一下进程与线程
进程是计算机调度资源的最小单位
线程进程调度资源的最小单位 一个进程至少有一个线程

java中创建线程的四种方式

1、继承Thread类,重写其中的run()方法,run方法中就是要执行的任务

优点:就是简单直接
缺点:就是一个类不能继承多个类,耦合性较高 首先创建一个MyTHread1类,这个类继承自Thread类,然后重写Thread类中的run方法,在run方法中输出100次线程的名字和你好

class MyThread1 extends Thread{
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("你好");			
		}
	}
}

在主函数中创建MyThread1对象,调用start()方法启动线程

Thread thread1 = new MyThread1();
thread1.start();

2、实现Runnable接口,重写其中的run方法,run中就是要执行的任务

优点:将任务与其他任务进行分割,降低了耦合性
首先创建Mythread2类,实现Runnable接口,并重写run方法,在run实现输出100次hello
world

class MyThread2 implements Runnable{

	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("hello world");
		}
	}
}

在主函数中创建MyThread2的对象,因为这里使用的是实现Runnable接口,所以需要先创建新线程,再调用start方法启动线程

Runnable thread2 = new MyThread2();
new Thread(thread2).start();

注意在这里如果直接在main函数中调用类中的run()方法的话,使用的是main线程,而不是自己创建的线程,这样的话,创建线程就没有意义了,所以需要使用start启动线程

接下来我们在同一个main方法中同时启动两个线程

Thread thread1 = new MyThread1();
		
		/*
		 * start();启动线程
		 * 开辟一条新的线程任务
		 */
thread1.start();
		
Runnable thread2 = new MyThread2();
new Thread(thread2).start();
		/*
		 * 直接执行run方法是用的main线程执行
		 */
		//thread1.run();

我们会发现输出的时候会出现交叉的情况,也就是说线程1执行输出了一部分线程1的名字和你好,就会出现线程2执行输出的helloworld,出现这种情况的原因是线程1在使用cpu时间片执行一部分输出之后,cpu时间片用完了,轮到了线程2使用cpu时间片

下面是前两个方法的完整代码,

public class ThreadDemo1 {
	public static void main(String[] args) {
		Thread thread1 = new MyThread1();
		
		/*
		 * start();启动线程
		 * 开辟一条新的线程任务
		 */
		thread1.start();
		
		Runnable thread2 = new MyThread2();
		new Thread(thread2).start();
		
		//运行会出现交叉的原因是cpu时间片用完了
		
		/*
		 * 直接执行run方法是用的main线程执行
		 */
		//thread1.run();
	}
}
//创建线程1继承Thread类
class MyThread1 extends Thread{
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("你好");			
		}
	}
}

//创建线程2实现Runnable接口
class MyThread2 implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("hello world");
		}
	}
}

3、实现Callable接口,重写call()方法,call中就是线程任务

首先创建MyCallable类实现Callable接口,重写call()方法,Callable接口支持泛型,为了不报警告,需要在实现的时候加上数据类型
sleep()方法是使线程进入休眠状态(也就是阻塞状态)一定时间,之后再进入就绪状态

class MyCallable implements Callable<String>{
	@Override
	public String call()  {
		System.out.println("你好");
		return "任务执行结束";
	}	
}

前三种方法之间的区别是,继承Thread类和实现Runnable接口,重写的是run()方法,实现Callable接口重写的是call()方法并且run方法没有返回值,call方法有返回值所以当我们需要任务有返回值的时候就可以使用第三种方法

4、使用线程池创建线程

线程池的概念:存放空闲线程的集合
线程池的作用:提高了效率,减少了线程创建和关闭的时间
创建线程池使用的是Executors类,调用类中的方法即可创建线程池
线程池有四种创建方式

(1)、根据内存创建线程池:Executors.newCachedThreadPool()

优点:可以存储无限多条线程
缺点:太占内存了

首先根据内存创建线程池

ExecutorService pool = Executors.newCachedThreadPool();

调用execute方法给线程池一个任务:输出执行任务的线程的名字
这里创建了两个任务
exceute(command)方法中,参数里面就是要执行的任务,任务的创建可以使用匿名内部类的方法写进Runnable接口中的run方法中,也可以使用lambda表达式

public class ThreadPoolDemo01 {
	public static void main(String[] args) {
		//根据内存创建线程池,里面可以有无限多个线程
		ExecutorService pool = Executors.newCachedThreadPool();
		pool.execute(new Runnable() {
			public void run() {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName());
				
			}
		});
		
		pool.execute(()->{
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName());
		});
		//关闭线程池
		pool.shutdown();
	}
}

输出结果
在这里插入图片描述

(2)创建固定大小的线程池:Executors.newFixedThreadPool(线程条数)

线程条数固定,当有多个任务进入线程池,如果没有空闲线程,后面进入的任务会等待前面的任务执行完毕之后,线程空闲下来才会执行

创建一个只有3条线程的线程池

ExecutorService pool = Executors.newFixedThreadPool(3);

然后给线程池10个任务,每隔100毫秒输出一次是哪个线程再执行任务,用完之后关闭线程池

for(int i=0;i<10;i++) {
	pool.execute(()->{
		try {
			Thread.sleep(100);
			System.out.println(Thread.currentThread().getName()+":执行任务");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	});
	//关闭线程池
	pool.shutdown();
}

结果:在这里插入图片描述

(3)第三种方法创建的也是固定大小的线程池,但是这个线程池支持延迟及周期性任务:Executors.newScheduledThreadPool(线程条数)

创建一个只有三条线程的线程池

ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);

注意这里返回值类型要选择ScheduledExecutorService

创建延迟性线程任务使用的是schedule(command, delay, unit)方法
command:线程任务,该任务是Callable中的call方法任务;
delay:延迟时间,也就是在指定时间之后任务开始执行;
unit:时间单位,给定延迟时间的单位,使用的是TimeUnit类中的属性

pool.schedule(()->{
	//输出执行任务的线程名字
	System.out.println(Thread.currentThread().getName());
}, 1, TimeUnit.SECONDS);

创建周期性任务使用的是scheduleAtFixedRate(command, initialDelay,
period, unit)方法
command:线程任务
initialDelay:延迟时间(多久之后执行第一个任务)
period:周期时间(每隔多久执行一次任务)
unit:时间单位

pool.scheduleAtFixedRate(()->{
	System.out.println(Thread.currentThread().getName());
}, 1, 3, TimeUnit.SECONDS);

在这里插入图片描述

注意:周期性任务不能关闭线程池,不然没有效果

(4)创建单线程的线程池:Executors.newSingleThreadExecutor()

单线程线程池的特点就是执行的任务有先后顺序,先来先执行,后来后执行

public static void main(String[] args) {
	//创建一条线程的线程池,该线程执行有先后顺序
	ExecutorService pool = Executors.newSingleThreadExecutor();
	for(int i=0;i<5;i++) {
		pool.execute(()->{
			try {
				Thread.sleep(100);
				System.out.println(Thread.currentThread().getName());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});
	}
	//关闭线程池
	pool.shutdown();
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值