线程的编程4种实现方法

4 篇文章 0 订阅

继承Thread

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例

public class Thread implements Runnable
@FunctionalInterface //函数式接口,其中包含一个抽象方法run
public interface Runnable {
public abstract void run();
}

启动线程的唯一方法就是通过Thread类的start()实例方法,不能直接调用run()方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并覆写run()方法,就可以启动新线程并执行自己定义的run()方法。

class LeftThread extends Thread{
public void run() {
    for(int i=0;i<50;i++) {
		System.out.println("左手一个慢动作...");
	try {//使当前正在执行的线程休眠等待500ms
		Thread.sleep(500);
	} catch (InterruptedException e) {
		e.printStackTrace(); }
		}
	}
}
//在主线程中启动这个线程
Thread t1=new LeftThread();
t1.start();//不能直接调用run方法,否则就是在主线程中单线程执行,不是多线程

注意:接口方法的实现,覆盖定义时不能修改方法签名

public synchronized void start() {
	if (threadStatus != 0) //当前线程状态检查
		throw new IllegalThreadStateException();
	group.add(this); //将当前线程对象添加到一个线程组中进行管理
	boolean started = false; //标志值用于判断当前线程是否已经启动
	try {
		start0();
		started = true;
	} finally {
		try {
			if (!started) {
			group.threadStartFailed(this);
			}
		} catch (Throwable ignore) {
		}
	}
}
private native void start0();//通过对等类实现启动线程的细节,内部就是自动调用run()方法

定义线程类

class LeftThread extends Thread{
//定义左手线程类,只需要覆盖(重写)定义其中的run()方法,这里实际就是线程的执行线索,执行的程序
	public void run(){
		for(int i=0;i<50;i++)
			System.out.println("左手一个慢动作....")
	}
}

调用方法

Thread t1=new LeftThread();
t1.start();//启动线程必须使用start方法,而不能是调用run方法
//t1.run();不允许直接调用run方法,因为这种调用方式不是多线程,而是单线程
public class T4 {
public static void main(String[] args) {
new Thread() {
public void run() {
System.out.println("匿名类内部输出"+Thread.currentThread().getName());
}
}.run();//从语法上来说,可以直接调用run方法,但是运行run方法则不是多线程
//.start();输出结果证明不是main线程
System.out.println("main方法输出"+Thread.currentThread().getName());
}
}

判断线程运行情况

最简单的方式是查看线程个数
// Thread.currentThread()用于获取当前正在运行这个代码的线程对象
System.out.println(Thread.currentThread());
Thread t1 = new LeftThread();
// t1.run();在当前主线程中执行t1对象中的run方法
// t1.start();可以在LeftThread中获取到另外一个线程对象
public class LeftThread extends Thread {
@Override
public void run() {// 这里包含的就是执行线索
System.out.println(Thread.currentThread());
}
}

实现Runnable接口

接口定义

@FunctionalInterface //函数式接口,简化定义方式为lambda表达式
public interface Runnable{
public abstract void run();
}

可以使用lambda表示式进行定义,或者使用匿名内部类进行定义

class RightRunnable implements Runnable{
public void run(){
for(int i=0;i<50;i++)
System.out.println(Thread.currentThread().getName()+"说:右手慢动作重播!");
}
}

启动方法

//启动通过实现Runnable接口定义的线程对象,不能直接使用Runnable r=new RightRunnable();
Thread t2=new Thread(new RightRunnable());
t2.start();
//匿名内部类的写法
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread() + "--" + i);
}
}
});
t1.start();
//lambda表达式的写法---要求接口必须是函数式接口
Thread t1 = new Thread(()-> {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread() + "--" + i);
}
});
t1.start();

使用Callable和Future接口创建线程

具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程

接口定义
Callable接口用于定义线程的执行逻辑;
Future接口用于Callable接口中call方法的执行结果,并可以测试Callable的执行情况;
系统提供了一个Future接口的实现类FutureTask,有了FutureTask类则可以将Callable实现封装到FutureTask对象中,同时由于FutureTask实现了Runnable接口,所以可以作为Thread类的构造器参数传入Thread对象中,供线程执行对应call方法中的逻辑

使用方法:
1、 定义Callable接口的实现类

public class MyCallable implements Callable<Integer>{
int begin=0;
int end=0;
public MyCallable(int begin, int end){
this.begin=begin;
this.end=end;
}
public Integer call()throws Exception{
int res=0;
for(int k=begin; k<=end; k++)
res+=k;
System.out.println(Thread.currentThread()+"---"+res);
return res;
}
}

2.调用

FutureTask[] arr=new FutureTask[10];
for(int i=0;i<10;i++){
arr[i]=new FutureTask<>(new MyCallable(i*10+1,(i+1)*10));
Thread t=new Thread(arr[i]);
t.start();
}
int res=0;
for(int i=0;i<arr.length;i++)
res+=((Integer)arr[i].get());
System.out.println("计算结果为:"+res);

注意:FutureTask实现了Future和Runnable接口,所以new Thread(futureTask),当执行thread.start()方法时会自动调用Callable接口实现中的call方法。当调用futureTask.get()方法时可以获取对应的线程对象的执行结果,如果线程并没有返回时,当前线程阻塞等待。

使用线程池创建线程

  • 什么是线程池
    创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。
    从JDK1.5开始,Java API提供了Executor框架可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)
  • new Thread的弊端
    每次new Thread新建对象性能差。
    线程缺乏统一管理,可能无限制新建线程,相互之间竞争及可能占用过多系统资源导致死机或oom。
    四种线程池的好处
    重用存在的线程,减少对象创建、消亡的开销,性能佳
    可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
    提供定时执行、定期执行、单线程、并发数控制等功能。
  • 线程池的工作原理[***]
    1、线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。
    2、线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。
    如果工作队列满了,则执行第三步
    3、线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值