1.多线程的创建
Thread类
Java是通过java.lang.Thread类来代表线程的。
按照面向对象的思想,Thread类提供了实现多线程的方式。
1.1方式一:继承Thread类
多线程的实现方案一:继承Thread类
① 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法。
② 创建MyThread类的对象。
③ 调用线程对象的start()方法启动线程(启动后还是执行run方法的)。
方式一优缺点:
优点:编码简单。
缺点:线程类已经继承Thread,无法继承其他类,不利于扩展。
示例代码如下:
publicclassThreadDemo1 { publicstaticvoidmain(String[] args) { // 3.实例化线程对象Threadt=newMyThread(); // 4.调用start方法启动子线程(执行run方法)t.start(); for (inti=0; i<5; i++) { System.out.println("主线程执行输出:"+i); } } } /*** 1.自定义一个线程类继承Thread类*/classMyThreadextendsThread { /*** 2.重写run方法,其中是此线程要做的工作*/publicvoidrun() { for (inti=0; i<5; i++) { System.out.println("子线程执行输出:"+i); } } }
注:main方法中不是直接调用run方法,而是调用start方法启动线程中的run方法。
直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。
只有调用start方法才是启动一个新的线程执行。
注意将主线程任务放在子线程任务之后,否则主线程执行完毕后再执行子线程,相当于是一个单线程的效果。
1.2方式二:实现Runnable接口
多线程的实现方案二:实现Runnable接口
① 定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法。
② 创建MyRunnable任务对象。
③ 把MyRunnable任务对象交给Thread处理。
④ 调用线程对象的start()方法启动线程。
Thread类构造器
方法名 |
说明 |
public Thread(String name) |
可以为当前线程指定名称 |
public Thread(Runnable target) |
封装Runnable对象成为线程对象 |
public Thread(Runnable target ,String name ) |
封装Runnable对象成为线程对象,并指定线程名称 |
方式二优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。
示例代码如下:
publicclassThreadDemo2 { publicstaticvoidmain(String[] args) { // 3.创建一个任务对象Runnabletarget=newMyRunnable(); // 4.把任务对象交给Thread处理Threadt=newThread(target); // 5.启动线程t.start(); for (inti=0; i<5; i++) { System.out.println("主线程执行:"+i); } } } /*** 定义一个线程任务类,实现Runnable接口*/classMyRunnableimplementsRunnable { /*** 重写run方法,定义线程的执行任务*/publicvoidrun() { for (inti=0; i<5; i++) { System.out.println("子线程执行:"+i); } } }
多线程的实现方案二:实现Runnable接口(匿名内部类形式)
① 可以创建Runnable的匿名内部类对象。
② 交给Thread处理。
③ 调用线程对象的start()启动线程。
示例代码如下:
publicclassThreadDemo2Other { publicstaticvoidmain(String[] args) { newThread(() -> { for (inti=0; i<5; i++) { System.out.println("子线程执行:"+i); } }).start(); for (inti=0; i<5; i++) { System.out.println("主线程执行:"+i); } } }
1.3方式三:JDK 5.0新增:实现Callable接口
前2种线程创建方式都存在一个问题:他们重写的run方法均不能直接返回结果,不适合需要返回线程执行结果的业务场景。
怎么解决这个问题呢?JDK 5.0提供了Callable和FutureTask来实现,这种方式的优点是可以得到线程执行的结果。
多线程的实现方案三:利用Callable、FutureTask接口实现
①得到任务对象
1)定义类实现Callable接口,重写call方法,封装要做的事情。
2)用FutureTask把Callable对象封装成线程任务对象。
②把线程任务对象交给Thread处理。
③调用Thread的start方法启动线程,执行任务
④线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果。
FutureTask的常用API
方法名 |
说明 |
public FutureTask<>(Callable call) |
把Callable对象封装成FutureTask对象 (需要声明任务对象返回值的泛型) |
public V get() throws Exception |
获取线程执行call方法返回的结果 |
示例代码如下:
publicclassThreadDemo3 { publicstaticvoidmain(String[] args) { // 3.创建Callable任务对象Callable<String>call=newMyCallable(10); // 4.将Callable任务对象交给FutureTask对象,封装为真正的任务对象/*FutureTask对象的作用:实现了Runnable接口,可以交给Thread对象处理可以在线程执行完毕之后通过调用其get方法获取线程执行完毕的返回值*/FutureTask<String>f=newFutureTask(call); // 5.将任务对象交给Thread处理Threadt=newThread(f); // 6.启动线程t.start(); Callable<String>call2=newMyCallable(20); FutureTask<String>f2=newFutureTask(call2); Threadt2=newThread(f2); t2.start(); try { // 若任务没有执行完毕,则代码会在这里等待,直至线程执行完毕,再执行get方法,获取返回值Stringrs=f.get(); System.out.println("第1个结果:"+rs); } catch (Exceptione) { e.printStackTrace(); } try { Stringrs2=f2.get(); System.out.println("第2个结果:"+rs2); } catch (Exceptione) { e.printStackTrace(); } } } /*** 1.定义一个任务类,实现Callable接口,需要声明泛型,表明返回值类型*/classMyCallableimplementsCallable<String> { privateintn; publicMyCallable(intn) { this.n=n; } /*** 2.重写call方法(任务方法) 此处为求和方法** @return* @throws Exception*/publicStringcall() throwsException { intsum=0; for (inti=1; i<=n; i++) { sum+=i; } return"子线程执行结果"+sum; } }
注:若任务没有执行完毕,则代码会在获取返回值的get方法处等待,直至线程执行完毕,再执行get方法,获取返回值。
2.Thread的常用方法
Thread类常用方法
方法名 |
说明 |
String getName () |
获取当前线程的名称,默认线程名称是Thread-索引 |
void setName (String name) |
将此线程的名称更改为指定的名称, 通过构造器也可以设置线程名称 |
public static Thread currentThread() |
返回对当前正在执行的线程对象的引用 |
public static void sleep(long time) |
让当前线程休眠指定的时间后再继续执行,单位为毫秒 |
public void run() |
线程任务方法 |
public void start() |
线程启动方法 |
示例代码如下:
自定义MyThread类
publicclassMyThreadextendsThread { publicMyThread() { } publicMyThread(Stringname) { // 为当前线程对象设置名称,交给父类有参构造器初始化super(name); } publicvoidrun() { for (inti=0; i<5; i++) { System.out.println(Thread.currentThread().getName() +"输出:"+i); } } } 测试类publicclassThreadDemo1 { publicstaticvoidmain(String[] args) throwsInterruptedException { Threadt=newMyThread("1号线程"); t.start(); Threadt2=newMyThread("2号线程"); t2.start(); // 哪个线程执行它,它就得到哪个线程对象(当前线程对象)Threadm=Thread.currentThread(); System.out.println(m.getName()); // 主线程名称为"main"for (inti=0; i<5; i++) { if (i==2) { // 让线程休眠3sThread.sleep(3000); } System.out.println("main线程输出:"+i); } } }
程序运行结果如下:
main
main线程输出:0
main线程输出:1
1号线程输出:0
1号线程输出:1
1号线程输出:2
1号线程输出:3
1号线程输出:4
2号线程输出:0
2号线程输出:1
2号线程输出:2
2号线程输出:3
2号线程输出:4
main线程输出:2
main线程输出:3
main线程输出:4