package com.fuping3.basic;
/**
* 实现多线程方式一
* 优点:编写简单,可直接用this.getname()获取当前线程,不必使用Thread.currentThread()方法。
* 缺点:
* 1、使用继承方式,MyThread不能再继承其他类
* 2、无返回值
*/
public class MyThread extends Thread{
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
System.out.println("Thread--当前运行线程:"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//创建Thread类的子类的对象
MyThread t=new MyThread();
/**
* 通过此对象调用start():①启动当前线程 ② 调用当前线程的run()
*
* 注意点一:我们不能通过直接调用run()的方式启动线程。
* t.run();
*/
t.start();
/**
* 注意点二:再启动一个线程,不可以还让已经start()的线程去执行,否则抛异常IllegalThreadStateException
* t.start();
* 解决:我们需要重新创建一个线程的对象
*/
MyThread t2=new MyThread();
t2.start();
}
}
二、实现Runnable接口
package com.fuping3.basic;
/**
* 实现多线程方式二--推荐
* 优点:避免了单继承的局限性、多个线程可以共享一个Runnable对象
* 缺点:
* 1、无返回值
* 2、访问线程必须使用Thread.currentThread()方法
*/
public class MyRunnable implements Runnable{
public void run() {
while(true){
try {
Thread.sleep(1000);
System.out.println("Runnable--当前运行线程:"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//创建实例对象
MyRunnable runnable=new MyRunnable();
//须将Runnable对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1=new Thread(runnable);
//通过Thread类的对象调用start()
t1.start();
/**
* 再启动一个线程--可以共享Runnable实例对象
*
*/
Thread t2=new Thread(runnable);
t2.start();
}
}
三、实现Callable接口
package com.fuping3.basic;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 实现多线程方式三--推荐
* 优点:
* 1、避免了单继承的局限性、多个线程可以共享一个Callable对象
* 2、有返回值
* 缺点:
* 1、比较复杂
* 2、访问线程必须使用Thread.currentThread()方法
*/
public class MyCallable implements Callable<Integer> {
public Integer call() throws Exception {
int end= new Random().nextInt(10);
for(int i=0;i<=end;i++){
Thread.sleep(1000);
}
System.out.println("Callable--当前运行线程:"+Thread.currentThread().getName()+"--结束数字:"+end);
return end;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Callable实例对象
MyCallable callable=new MyCallable();
/**
* 创建接收结果对象
* 1、FutureTask实现Runnable、Future接口
* (1)实现Runnable接口,具有多线程功能
* (2)实现Future接口,具有接收返回值功能
*/
FutureTask<Integer> futureTask=new FutureTask(callable);
//须将Callable对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1=new Thread(futureTask);
t1.setName("线程A");
//通过Thread类的对象调用start()
t1.start();
/**
* 再启动一个线程--可以共享Runnable实例对象
*
*/
FutureTask<Integer> futureTask2=new FutureTask(callable);
Thread t2=new Thread(futureTask2);
t2.setName("线程B");
t2.start();
//
/**
* 接收返回值
* 注意点:get()为同步阻塞的,即阻塞一直到拿到返回值,程序才往下继续执行
*/
System.out.println("线程A返回值:"+futureTask.get());
System.out.println("线程B返回值:"+futureTask2.get());
}
}
备注一、为什么线程的启动不直接使用run()而必须使用start()呢?
1、如果直接调用run()方法,相当于就是简单的调用一个普通方法,不会开启多线程。
2、run()的调用是在start方法中的start0()这个Native C++方法里调用的
备注二、FutureTask既可以拿到线程正常执行时返回的值,也可以拿到线程执行异常时的异常信息(举例见线程池异常处理)
四、线程池创建线程
4.1 使用线程池的好处
我们之前使用线程的时候都是使用new Thread来进行线程的创建,但是这样会有一些问题:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 提供定时执行/定期执行、并发数控制功能( 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞)。