Java ----- 线程(Thread)(二)创建线程
Java程序中的 public static void main() 方法时主线程的入口,当运行Java程序时,会先执行这个方法。
程序运行的时候系统(cpu)会分配一个进程用于执行该程序,在Java中,即使在运行的程序中没有创建线程,后台也会有多个线程运行,如主线程、gc 线程,其中主线程为main函数是程序入口,用于执行整个程序,gc 为jvm 的垃圾回收机制,他们是相互独立的执行路径,当开辟了多个线程时,线程的运行由调度器进行安排调度,线程的先后顺序是不能人为干预的,因为调度器是操作系统的cpu进行调度,因此线程会增加额外的开销,可以通过并发进行控制开销。并发还有个好处是,可以避免在对同一份资源进行操作时,存在的资源抢夺问题,在每个线程在自己的工作内交互时,内存控制不当会造成数据不一致。
注意:
(1)、每个程序至少自动拥有一个线程,称为主线程。
(2)、当程序加载到内存时启动主线程。
(3)、开发中用户编写的线程一般是指除了主线程之外的其他线程,主线程调用start()方法,子线程执行run 方法,多条执行路径,主线程与子线程交替执行。
使用一个线程的主要步骤:
(1)、定义一个线程,同时指明这个线程索要执行的代码,即期望完成的功能
(2)、创建线程对象
(3)、启动线程
(4)、终止线程
Thread 类常用的方法:
java 提供了java.lang.Thread 类支持多线程编程,该类提供了大量的方法来控制和操作线程,如下
定义一个线程类常见的两种方法:继承java.lang.Thread 类和实现java.lang.Runnable 接口
1、使用Thread 类创建线程
创建线程时继承Thread 类并重写Thread类的run 方法。Thread 类的run 方法是线程要执行操作任务的方法,所以线程要执行的操作代码都需要写在run() 方法中,并通过调用start() 方法来启动线程。
使用继承Thread 类的方式创建线程主要步骤
(1)、定义一个类继承Thread 类,重写run 方法,
(2)、重写run 方法,并在方法中写需要输出的数据
(3)、创建线程对象
(4)、调用start() 方法启动线程
注意:创建对象时不会执行线程,必须调用线程对象的start() 方法才会使线程开始运行。
package com.obge.threadstu; public class ThreadStu extends Thread { private int icount =0; //重写run 方法 public void run(){ // while(icount <5){ // icount ++; // System.out.println("while的值:"+icount); // } do{ icount ++; System.out.println("do_while的值:"+icount); }while (icount<4); // for(;icount<3;icount++){ // System.out.println("for的值:"+icount); // } } } package com.obge.threadstu; public class TestThread { public static void main(String[] args) { //实例化线程对象 ThreadStu threadStu = new ThreadStu(); threadStu.start(); } }
缺点:虽然,使用继承Thread的方式简单明了,但是如果定义的类已经继承了其他类则无法在继承Thread 类。所以引入了另一种方式实现Runnable 接口创建线程
在来一个小例子:借助线程从网络中下载文件
2、使用Runnable 接口创建线程
Runnable 接口中声明了一个run()方法(即public void run() )
一个类可以通过实现Runnable 接口并实现其 run() 方法完成线程的所有活动,其中以实现的run() 方法称为该对象的线程体。任何实现Runnable 接口的对象都可以作为一个线程的目标对象。
使用实现Runnable 接口方式创建对象的主要步骤:
(1)、定义一个类实现java.lang.Runnable 接口
(2)、实现该接口的run()方法,并在方法中写需要输出的数据
(3)、创建线程对象
(4)、调用start() 方法启动线程
使借助Thread thread = new Thread( new RunnableStu());创建对象
package com.obge.threadstu; public class RunnableStu implements Runnable{ //重写注解 一个内建注解 @Override public void run() { for(int i = 0;i<3;i++){ System.out.println("for的值:"+i); } } } package com.obge.threadstu; public class TestThread { public static void main(String[] args) { //实例化线程对象 //ThreadStu threadStu = new ThreadStu(); Thread thread = new Thread( new RunnableStu()); thread.start(); } }
另一个小例子
两种方式比较:
直接继承Thread 类的方式编写简单,可以直接操作线程,适合单重继承的情况
实现Runnable接口方式,当一个线程继承另一个类时,只能实现Runnable 接口的方法创建线程,使多个线程之间使用同一个Runnable对象(因为java 只能单继承,可以实现多个接口)
3、实现Callable 接口
主要步骤:
(1)、实现Callable 接口,需要返回值类型
(2)、重写call 方法 ,需要抛出异常
(3)、创建目标对象
(4)、创建执行服务 :ExecutorService executorService = Executors.newFixedThreadPool();
(5)、提交执行:Future<Boolean> result1 = executorService.submit(thread1);
(6)、获取结果:boolean r1 = result1.get();
(7)、关闭服务:executorService.shutdown();
package com.obge.threadstu; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /* * Callable 可以抛出异常也可以定义返回值 */ public class CallableStu implements Callable<Boolean>{ //声明创建变量 private String url; private String name; //提供构造方法,用于对象创建,对类进行初始化 public CallableStu(String url,String name) { this.url = url; this.name = name; } //通过实现Callable 方式,创建线程 @Override public Boolean call() throws Exception { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloads(url, name); System.out.println("下载图片的名字:"+name); return true; } //程序入口 public static void main(String[] args) throws InterruptedException, ExecutionException { //实例化线程 CallableStu thread1 = new CallableStu("https://img.alicdn.com/imgextra/i4/121853076/O1CN01kOYLk81Yaqvw2jDOc_!!0-saturn_solar.jpg_468x468q75.jpg_.webp","img1.jpg"); CallableStu thread2 = new CallableStu("https://mirrors.bfsu.edu.cn/apache//commons/io/binaries/commons-io-2.8.0-bin.zip","file1.jpg"); CallableStu thread3 = new CallableStu("https://img.alicdn.com/imgextra/i4/15012183/O1CN01yDHHYJ1RzrGb2Up4z_!!0-saturn_solar.jpg_468x468q75.jpg_.webp","img2.jpg"); CallableStu thread4 = new CallableStu("https://img.alicdn.com/imgextra/i3/127372649/O1CN01dadNEf1VRHpmz7f66_!!0-saturn_solar.jpg_468x468q75.jpg_.webp","img3.jpg"); //线程执行,其顺序由不能控制 //创建执行服务,将四个线程放到池子里 ExecutorService executorService = Executors.newFixedThreadPool(4); //提交执行 Future<Boolean> result1 = executorService.submit(thread1); Future<Boolean> result2 = executorService.submit(thread2); Future<Boolean> result3 = executorService.submit(thread3); Future<Boolean> result4 = executorService.submit(thread4); //获取结果 boolean r1 = result1.get(); boolean r2 = result2.get(); boolean r3 = result3.get(); boolean r4 = result4.get(); System.out.println("打印结果为:r1="+r1+",r2="+r2+",r3="+r3+",r4="+r4); //关闭服务 executorService.shutdown(); } }
下载类
package com.obge.threadstu; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import org.apache.commons.io.FileUtils; //下载类 public class WebDownloader { //下载方法 public void downloads(String url,String name) { try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }