线程的创建
java.lang.Thread的构造方法API
创建线程的方式最终都是通过调用下面代码来创建线程:
/**
* group - 线程组。 如果null并且有一个安全管理器,该组由SecurityManager.getThreadGroup()确定 。 如果没有安全管理员或SecurityManager.getThreadGroup()返回null ,该组将设置为当前线程的线程组。
* target - 启动此线程时调用其run方法的对象。 如果null ,这个线程的run方法被调用。
* name - 新线程的名称
* stackSize - 新线程所需的堆栈大小,或为零表示此参数将被忽略。
*/
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
以Runnable
接口是否在构造方法中传入为区分,上述构造方法分成两类创建线程对象:
- 继承java.lang.Thread类,通过子类重写run方法实现业务逻辑处理后创建子类线程对象。
- 入参传入java.lang.Runnable接口,当然可以使用Thread内的
Runnable target
,也可以使用自定义的Runnable接口实例。
然后这样分析的话很容易理解,传统意义上的,线程的创建有两种方式:
- 1.继承java.lang.Thread类;不推荐使用,OOP单继承局限性。
- 2.实现java.lang.Runnable接口;推荐使用,避免OOP单继承局限性,灵活方面,适合于多个线程处理同一资源的情况,比如买票,取号等。
线程创建与执行步骤:
- 1.实现接口java.lang.Runnable重写其run方法;
其实java.lang.Thread也是实现了java.lang.Runnable接口。子类使用继承java.lang.Thread类形式创建线程,子类也重写了run方法,如果不重写,没有新创建线程的实际意义。
-
2.创建线程对象:继承Thread类通过创建子类对象,实现Runnable接口通过new Thread(runnable)创建线程对象。
-
3.通过start()方法启动线程。
从设计模式上看,java.lang.Thread
和java.lang.Runnable
实际上是一种静态代理的实现方式。
方式一:继承java.lang.Thread类
继承Thread类的话,重写run方法,在run方法中定义需要执行的任务。
通过继承继承Thread类创建自己的线程 :
package com.thread.study;
public class Test {
public static void main(String[] args) {
System.out.println("执行主线程名称:"+Thread.currentThread().getName());
MyThread thread1 = new MyThread();
thread1.start();
MyThread thread2 = new MyThread();
thread2.run();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("当前执行的线程名:"+Thread.currentThread().getName());
}
}
运行结果:
执行主线程名称:main
当前执行的线程名:main
当前执行的线程名:Thread-0
结论:
- 通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别
- 虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。
方式二:实现java.lang.Runnable接口
通过实现Runnable接口来实现必须重写其run方法,在run方法中定义需要执行的任务。
package com.thread.study;
public class TestRunnable {
public static void main(String[] args) {
System.out.println("执行主线程名称:"+Thread.currentThread().getName());
MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();
new Thread(runnable,"张三").start();
new Thread(runnable,"李四").start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("当前执行的线程名:"+Thread.currentThread().getName());
}
}
必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。
扩展:JDK针对Thread与Runnable的API
java.lang.Thread
public class Thread implements Runnable
线程是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的
Thread
对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为
main
的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:
- 已经调用了
Runtime
类的exit
方法,并且安全管理器已经允许进行退出操作。- 所有不是守护进程线程的线程都已经死亡,无论是从调用返回到
run
方法还是抛出超出run
方法的run
。
java.lang.Runnable
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
-
Runnable
接口应由任何类实现,其实例将由线程执行。该类必须定义一个无参数的方法,称为run
。 -
Runnable
接口旨在为希望在活动时执行代码的对象提供一个通用协议。
创建线程实现图片下载实例
package com.thread.study;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class TestDownLoadPictures {
public static void main(String[] args) {
PictureThread pictureThread = new PictureThread("https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png","E:\\study\\resource\\1.png");
PictureThread pictureThread2 = new PictureThread("https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png","E:\\study\\resource\\2.png");
PictureThread pictureThread3 = new PictureThread("https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png","E:\\study\\resource\\3.png");
new Thread(pictureThread,"1.百度").start();
new Thread(pictureThread2,"2.新浪").start();
new Thread(pictureThread3,"3.搜狗").start();
}
}
class PictureThread implements Runnable {
private String picUrl;
private String fileName;
public PictureThread(String picUrl, String fileName) {
this.picUrl = picUrl;
this.fileName = fileName;
}
@Override
public void run() {
try {
FileUtils.copyURLToFile(new URL(picUrl), new File(fileName));
System.out.println(Thread.currentThread().getName() + "执行" + picUrl + "的下载");
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行结果:
3.搜狗执行https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png的下载
1.百度执行https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png的下载
2.新浪执行https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png的下载
使用线程池方式—Callable接口
JDK关于Callable的API
@FunctionalInterface public interface Callable<V> { V call() throws Exception; }
返回结果并可能引发异常的任务。实现者定义一个没有参数的单一方法,称为
call
。Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,Runnable不返回结果,也不能抛出被检查的异常。
JDK关于ExecutorService(线程池类)中submit(Callable<T> task)方法的API
<T> Future<T> submit(Callable<T> task)
提交值返回任务以执行,并返回代表任务待处理结果的Future。 Future的get方法将在成功完成后返回任务的结果。
如果您想立即阻止等待任务,您可以使用result = exec.submit(aCallable).get();
格式的
result = exec.submit(aCallable).get();
使用步骤:
1.创建线程池对象 eg:ExecutorService service = Executors.newFixedThreadPool(2);
2.创建Callable接口子类对象 eg:class MyCreateCallable implements Callable
3.提交Callable接口子类对象 eg:Future<String> future = service.submit(myCreateCallable)
4.关闭线程池 eg:service.shutdown();
使用示例:
package com.thread.study;
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
//创建Callable对象
MyCreateCallable myCreateCallable = new MyCreateCallable();
Future<String> future = service.submit(myCreateCallable);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
try {
System.out.println("使用Callable接口返回值:"+future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
MyCreateRunnable myCreateRunnable = new MyCreateRunnable();
//使用获取个教练
service.submit(myCreateRunnable);
//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
//关闭线程池
service.shutdown();
}
}
// Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果
class MyCreateCallable implements Callable {
@Override
public String call() throws Exception {
System.out.println("我要一个教练:call");
Thread.sleep(2000);
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
return "我是通过Callable创建的";
}
}
// Runnable接口实现类,无法返回执行结果
class MyCreateRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练:call");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}
执行结果:
我要一个教练:call
教练来了: pool-1-thread-1
教我游泳,交完后,教练回到了游泳池
使用Callable接口返回值:我是通过Callable创建的
我要一个教练:call
教练来了: pool-1-thread-2
教我游泳,交完后,教练回到了游泳池
网友评论