JDK1.5之前创建线程的两种方法
1. 线程创建的方式一:继承Thread类
public class ThreadEstablish1 {
/*
JDK1.5之前创建线程的两种方法:
线程创建的方式一:继承Thread类
1.创建一个实现了Thread类的子类
2.实现类重写Thread类中的run() --> 将此线程要执行的操作声明在run()的方法体中
3.创建一个实现类对象
4.通过该对象调用start()
比如:遍历100以内的所有的偶数
*/
public static void main(String[] args) {
//此步骤还是main()主线程做的
subThread st = new subThread();
//此时才启动了分线程
//1.启动当前线程 2.调用当前线程的run()方法
st.start();
//问题一:不能通过直接调用run()的方式启动线程。此方式只相当于调用了一个普通方法而已
// st.run();
//问题二:在启动一个线程,遍历100以内的所有的偶数时,不能使用调用过start()方法的对象,
// 再去调用start(),如果调用会报IllegalThreadStateException异常
// st.start();
//问题二解决方法:再创建一个实现了Thread类的对象,通过此对象调用start()即可启动一个新线程。
subThread st1 = new subThread();
st1.start();
//问题三:创建Thread类的匿名子类的方式,创建线程
new Thread(){
@Override
public void run() {
for (int i = 0;i < 100;i++){
if (i % 2 == 0){
System.out.println("我是匿名子类线程:"+i);
}
}
}
}.start();
//主线程
for (int i = 0;i < 100;i++){
if (i % 2 == 0){
//Thread.currentThread().getName()获取当前执行的线程名
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
//1.创建一个实现了Thread类的子类
class subThread extends Thread{
@Override
public void run() {
for (int i = 0;i < 100;i++){
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
注意点:
- 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
- run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
- 想要启动多线程,必须调用start方法。
- 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。
2. 线程创建的方式二:实现Runnable接口的方式
public class ThreadEstablish2 {
/*
JDK1.5之前创建线程的两种方法:
线程创建的方式二:实现Runnable接口的方式
1.定义子类,实现Runnable接口。
2.子类中重写Runnable接口中的run方法。
3.通过实现Runnable接口的类,创建对象。
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
5.调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法.
*/
public static void main(String[] args) {
//3.通过实现Runnable接口的类,创建对象。
ThreadRunnable tr = new ThreadRunnable();
//4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
Thread thread = new Thread(tr);
//5.调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法.
thread.start();//1.启动当前线程 2.调用当前线程的run()方法 --> 调用了Runnable类型的target的run()
//在启动一个线程,遍历100以内的偶数
Thread thread1 = new Thread(tr);
thread1.start();
}
}
//1.定义子类,实现Runnable接口。
class ThreadRunnable implements Runnable{
//2.子类中重写Runnable接口中的run方法。
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
问题一:继承方式和实现方式的联系与区别?
两者联系:public class Thread extends Object implements Runnable
区别:
- 继承Thread:线程执行代码存放在Thread子类run方法中。
- 实现Runnable:线程执行代码存放在Runnable接口的实现类重写的run方法中。
实现方式的好处:
- 避免了单继承的局限性
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源(线程的共享数据)。
JDK1.5新增的创建线程有两种方法
3. 线程创建的方式三:实现Callable接口的方式
public class ThreadEstablish3 {
/*
线程创建的方式三:实现Callable接口的方式
1.创建实现了Callable接口的实现类
2.该实现类重写Callable接口中的call() --> 此call方法有返回值
3.创建该实现类的对象作为参数,传递给FutureTask的构造器,并创建FutureTask类的对象
4.将FutureTask类的对象作为参数,传递给Thread的构造器,创建Thread对象,并启动start()
5.如果需要获取call的返回值,可通过FutureTask类的对象调用get方法
get()的返回值,是FutureTask构造器的参数Callable接口实现call()的返回值
*/
public static void main(String[] args) {
//3.创建该实现类的对象作为参数,传递给FutureTask的构造器,并创建FutureTask类的对象
call call = new call();
FutureTask<Integer> futureTask = new FutureTask(call);
//4.将FutureTask类的对象作为参数,传递给Thread的构造器,创建Thread对象,并启动start()
Thread thread = new Thread(futureTask);
thread.start();
try {
//5.获取call的返回值
Integer in = futureTask.get();
System.out.println("sum总计为:"+in);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1.创建实现了Callable接口的实现类
class call implements Callable<Integer>{
int sum;
//2.该实现类重写Callable接口中的call() --> 此call方法有返回值
@Override
public Integer call() throws Exception {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
sum += i;
}
}
return sum;
}
}
**面试题:**如何理解实现了callable接口的方式创建线程 比 实现runnable接口的方式创建线程 要好?
- call()相比run()方法,可以有返回值
- call()方法可以抛出异常
- call()方法支持泛型的返回值
4. 线程创建的方式四:创建线程池的方式
1.线程池出现的背景: 经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。# 系列文章目录
2.多线程的解决方案:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
3.使用线程池的好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理:corePoolSize:核心池的大小、maximumPoolSize:最大线程数、
keepAliveTime:线程没有任务时最多保持多长时间后会终止
public class ThreadEstablish4 {
/*
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
4.1 ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
1. void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
2. <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable
3.void shutdown() :关闭连接池
4.2 Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
1. Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
2. Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
3. Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
4. Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
*/
public static void main(String[] args) {
//1.创建一个提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(20);
//2.执行指定的线程操作。需要提供实现了Runnable接口 或者 Callable接口的实现类的对象
run r = new run();
//execute()适用于Runnable接口
service.execute(r);
service.execute(r);
//3.关闭连接池
service.shutdown();
}
}
class run implements Runnable{
@Override
public void run() {
// synchronized (this) {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
// }
}
}
@仅为julius个人学习笔记