建立并启动线程的6种方式java
1.1继承Thread类方式建立线程的实现步骤:spring
步骤:数据库
1) 定义一个类A继承于java.lang.Thread类设计模式
2)在A类中覆盖Thread类中的run方法缓存
3)咱们在run方法中编写须要执行的操做---->run方法里的,线程执行体网络
4)在main方法(线程)中,建立线程对象,并启动线程数据结构
注意:千万不要调用run方法,若是调用run方法比如是对象调用方法,依然仍是只有一个线程,并无开启新的线程。多线程
1.2需求:使用两个线程实现边听歌边打游戏框架
实现代码:异步
//音乐线程 public class MusicThread { public static void main(String[] args) { //建立游戏线程对象 GameThread game = new GameThread(); //启动游戏线程 game.start(); while(true){ System.out.println(Thread.currentThread().getName()+"听音乐!"); } } } //游戏线程 class GameThread extends Thread{ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName()+"打游戏!"); } } }
注意:有的小伙伴可能以为音乐线程没有启动,在这里其实音乐线程已经启动起来了,而启动音乐线程的对象就是咱们的JVM,此处main方法其实启动的时候会建立一个主线程去执行main方法,因此我在这里使用主线程做为了个人音乐线程。
2.1实现Runnable接口方式建立线程的实现步骤:
1)定义一个类A实现于java.lang.Runnable接口,注意A类不是线程类。
2)在A类中覆盖Runnable接口中的run方法。
3) 咱们在run方法中编写须要执行的操做---->run方法里的,线程执行体。
4)在main方法(线程)中,建立线程对象,并启动线程。
2.2需求:使用两个线程实现边听歌边打游戏
实现代码:
//音乐线程 public class MusicThread { public static void main(String[] args) { //建立游戏线程对象 Thread game = new Thread(new Game()); //启动游戏线程 game.start(); while(true){ System.out.println(Thread.currentThread().getName()+"听音乐!"); } } } //游戏 class Game implements Runnable{ @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName()+"打游戏!"); } } }
2.3 继承方式和实现方式的区别
1)继承方式是一个类继承了Thread后成为线程类的子类,实现方式是一个类实现Runnable接口,可是这个类不是线程类,由于该类没有start等方法。
2)启动的时候继承方式直接调用本身的start方法,实现方式是借助了Thread中的start方法启动的,自身没有start方法。
3)继承方式调用的run方法是经过方法覆盖,经过继承方式实现的,运行的时候先找子类,没有最后才运行父类的run方法。实现方式是执行Thread的run方法,而Thread中的run方法调用了实现类中的run方法,使用过组合关系的方法调用实现的。
3.1使用Callable和FutureTask建立线程的实现步骤:
1)定义一个Callable接口的实现类
2)建立Callable实现类对象传递给FutureTask构造器
3)将FutureTask对象传递给Thread构造器
4)Thread对象调用start方法启动线程
5)经过FutureTask对象的get方法获取线程运行的结果
注意:
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时能够经过get方法获取执行结果,该方法会阻塞直到任务返回结果。
使用场景:使用多线程计算结果并返回该结果。
3.2需求:使用2个线程异步计算1-1000,000内之和
实现代码:
public class CallableDemo { public static void main(String[] args) throws Exception { //1.建立并启动线程 Callable<Integer> call1 = new CallableImpl(0, 50000); Callable<Integer> call2 = new CallableImpl(50001, 100000); FutureTask<Integer> f1 = new FutureTask<>(call1); FutureTask<Integer> f2 = new FutureTask<>(call2); new Thread(f1).start(); new Thread(f2).start(); //2.获取每个线程的结果 int ret1 = f1.get(); int ret2 = f2.get(); int ret= ret1+ret2; System.out.println(ret); } } class CallableImpl implements Callable<Integer>{ private int min; private int max; public CallableImpl(int min, int max) { this.min = min; this.max = max; } @Override public Integer call() throws Exception { int sum = 0; for (int i = min; i <= max; i++) { sum+=i; } return sum; } }
3.3Callable和Runnable的区别以下:
Callable定义的方法是call,而Runnable定义的方法是run。
Callable的call方法能够有返回值,而Runnable的run方法不能有返回值。
Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常。
注意:
FutureTask为Runnable的实现类
FutureTask能够视为一个闭锁(门闩),由于只有当线程运行完才会出现结果。
线程池,顾名思义就是一个池子里面放了不少的线程,咱们用就将线程从里面拿出来,使用完毕就放回去池子中。设计和数据库链接池类似,存在静态工厂方法用于建立各类线程池。
操做步骤:
1)使用Executors工具类中的静态工厂方法用于建立线程池
newFixedThreadPool:建立可重用且固定线程数的线程池,
newScheduledThreadPool:建立一个可延迟执行或按期执行的线程池
newCachedThreadPool:建立可缓存的线程池
2)使用execute方法启动线程
3)使用shutdown方法等待提交的任务执行完成并后关闭线程。
代码演示以下:
public class Demo4 { public static void main(String[] args) { Executor executor = Executors.newFixedThreadPool(5); executor.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); ((ExecutorService) executor).shutdown(); } }
在Spring3以后,Spring引入了对多线程的支持,若是你使用的版本在3.1之前,应该仍是须要经过传统的方式来实现多线程的。从Spring3同时也是新增了Java的配置方式,并且Java配置方式也逐渐成为主流的Spring的配置方式。
代码演示以下:
导入的包:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.1.0.RELEASE</version> </dependency> </dependencies>
配置类:
@Configuration @ComponentScan("cn.wolfcode") @EnableAsync //容许使用异步任务 public class SpringConfig {}
服务类:
@Service public class SpringService { @Async // 这里进行标注为异步任务,在执行此方法的时候,会单独开启线程来执行 public void dowork1() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } @Async public void dowork2() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } }
测试类:
public class SpringThreadDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); SpringService bean = context.getBean(SpringService.class); bean.dowork1(); bean.dowork2(); } }
注意:此时会出现一个DEBUG信息
在这里DEBUG信息不是什么错误,不会影响代码的正常运行,其实能够不用管的,可是为何出现这个问题呢?
Spring的定时任务调度器会经过BeanFactory.getBean的方法来尝试获取一个注册过的TaskExecutor对象来作任务调度,获取不到TaskExecutor对象再尝试找ScheduledExecutorService 对象,都找不到就报DEBUG信息。报错以后就找本身自己默认的scheduler定时器对象,这个举动实际上是作一个提醒做用,因此若是没有强迫症能够不用管它。
解决Spring使用多线程的报错信息
强迫症患者想要解决怎么办,三种方式:
严格来讲定时器(Timer)不是线程,他只是调度线程的一种工具,它里面封装了一个线程,因此咱们可使用定时器来使用线程。
操做步骤:
1)建立Timer 对象
2)调用schedule方法
3)传入TimerTask子类对象
代码演示以下:
Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "-->" + i); } } }, 100, 100);
文源网络,仅供学习之用,若有侵权,联系删除。我将优质的技术文章和经验总结都聚集在了个人公众号【Java圈子】里。
为方便你们学习,我还整理了一套学习资料,涵盖Java虚拟机、spring框架、Java线程、数据结构、设计模式等等,免费提供给热爱Java的同窗~