创建线程的两种方式
方式一:继承Thread类
1) 定义子类继承
Thread
类。
2)
子类中重写
Thread
类中的
run
方法。
3) 创建
Thread
子类对象,即创建了线程对象。
4) 调用线程对象
start
方法:①启动当前线程 ②调用当前线程的run()
例:遍历0到100的所有偶数
//1.创建一个继承于Thread类的子类
class MyThread extends Thread {
//2.重写Thread类的run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3.创建Thread类的子类的对象
MyThread t1 = new MyThread();
//4.调用start方法
t1.start();
}
}
问题一:我们不能通过直接调用run()的方式启动线程。
问题二:再启动一个线程,遍历100以内的偶数。
不可以让已经start()的线程去执行,会报IllegalThreadException,需要重新创建一个线程对象
方式二:实现Runnable接口
1. 创建一个实现了Runnable接口的类
2. 实现类去实现Runnable中的抽象方法,run()
3. 创建实现类的对象
4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5. 通过Thread类的对象调用start()
例:
//1. 创建一个实现了Runnable接口的类
class MThread implements Runnable{
//2. 实现类去实现Runnable中的抽象方法,run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
//3. 创建实现类的对象
MThread mThread = new MThread();
//4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(mThread);
// 5. 通过Thread类的对象调用start():①启动线程②调用当前线程的run()-->调用了Runnable类型的target
t1.start();
}
}
比较创建线程的两种方式:
开发中:优先选择,实现Runnable接口的方式
原因;1. 实现的方式没有类的单继承性的局限性。
2. 实现的方式更适合来处理多个线程有共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
方式三:实现Callable接口(jdk5.0新增)
与使用Runnable相比, Callable功能更强大些
>相比run()
方法,可以有返回值
>方法可以抛出异常
>支持泛型的返回值
>需要借助
FutureTask
类,比如获取返回结果
Future接口
>可以对具体
Runnable
、
Callable
任务的执行结果进行取消、查询是否完成、获取结果等。
>FutrueTask
是
Futrue
接口的唯一的实现类
> FutureTask
同时实现了
Runnable, Future
接口。它既可以作为Runnable被线程执行,又可以作为
Future
得到
Callable
的返回值
例:返回100以内的偶数和偶数和
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0 ;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现了ide对象作为传递到FutureTask的构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值
Object sum = futureTask.get();
System.out.println("总和为:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
1.call( ) 可以有返回值
2.call( ) 可以抛出异常,被外面的操作捕获,获取异常的信息
3.Callable是支持泛型的
方式四:线程池
背景:
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:
提前
创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
>提高响应速度(减少了创建新线程的时间)
>降低资源消耗(重复利用线程池中线程,不需要每次都创建)
>便于线程管理
>corePoolSize
:核心池的大小
>maximumPoolSize:最大线程数
>keepAliveTime
:线程没有任务时最多保持多长时间后会终止
> 等等
例:分别返回100以内的奇数偶数
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" +i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" +i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//2.执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合使用于Runnable
service.execute(new NumberThread1());//适合使用于Runnable
// service.submit(Callable callable);//适合适用于Callable
//3.关闭连接池
service.shutdown();
}
}
线程池相关
API
JDK 5.0
起提供了线程池相关
API
:
ExecutorService
和
Executors
ExecutorService
:真正的线程池接口。常见子类
ThreadPoolExecutor
>void execute(Runnable command)
:执行任务
/
命令,没有返回值,一般用来执行Runnable
> <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable >void shutdown() :关闭连接池
Executors
:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
>Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
>Executors.newFixedThreadPool(n);
创建一个可重用固定线程数的线程池
>Executors.newSingleThreadExecutor()
:创建一个只有一个线程的线程池
>Executors.newScheduledThreadPool(n)
:创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行
Thread类的有关方法
>void start():
启动当前线程,调用当前线程的run()
>run():
通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明再此方法中
>String getName():
返回线程的名称
>void setName(String name)
:
设置该线程名称
>static Thread currentThread():
返回执行当前代码的线程。在
Thread
子类中就是this
,通常用于主线程和
Runnable
实现类
>static void yield()
:
线程让步/释放当前cpu执行权1
暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
若队列中没有同优先级的线程,忽略此方法
>join()
:
当某个程序执行流中调用其他线程的
join()
方法时,调用线程将被阻塞,直到 join()
方法加入的
join
线程执行完为止
低优先级的线程也可以获得执行
>static void sleep(long millis)
:
(
指定时间
:
毫秒
)
令当前活动线程在指定时间段内放弃对CPU
控制
,
使其他线程有机会被执行
,
时间到后重排队。
抛出InterruptedException
异常
>stop():
强制线程生命期结束,不推荐使用
>boolean isAlive()
:
返回
boolean
,判断线程是否还活着
线程的优先级
线程的优先级等级
>MAX_PRIORITY
:
10
>MIN _PRIORITY
:
1
>NORM_PRIORITY
:
5
涉及的方法
>getPriority() :
获取线程优先值
>setPriority(int newPriority) :
设置线程的优先级
说明
>线程创建时继承父线程的优先级
>低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用