对于很多想学习java的人来说,经常听别人说线程难,其实真正理解了线程后,一点都不会觉得线程难,这里我为大家梳理下线程的创建方式吧.
一.线程的创建方式有三种
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口(返回结果并且可能抛出异常的任务).
如果采用实现Callable接口接口的方式,返回结果并且可能抛出异常的任务,不利于开发,这里就不给大家介绍了.这里咋们主要说说采用继承Thread类和实现Runnable接口的方式来创建线程.
1.继承Thread类的方式
-
定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
-
创建Thread子类的实例,即创建了线程对象
-
调用线程对象的start()方法来启动该线程
代码如下:
测试类:
/* 1)定义类继承Thread,重写run方法 2)创建线程对象 3)进行调用线程的start方法进行开启线程 */ public class Demo01 { public static void main(String[] args) { MyThread m = new MyThread(); m.start(); for (int i = 0; i < 100; i++) { System.out.println("小强:" + i); } } }
创建类来实现Thread
public class MyThread extends Thread { @Override public void run() { //多线程处理的任务 for (int i = 0; i < 100; i++) { System.out.println("旺财:" + i); } } }
看了上面的代码,你可能会问,线程对象是如何创建的,线程里面有哪些方法呢?下面就来说说怎么创建线程对象.
通过查看API我们看到了lang包下的Thread类,是非抽象的,还有构造方法,那是不是就是可以直接用Thread类的构造方法来创建对象呢?答案是:不可以!!!
记住:
Thread类的构造方法不是为创建线程而定的,是给子类使用的!!!
线程常用的方法:
1. public String getName():获取当前线程名称。
2. public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。
3. public void run():此线程要执行的任务在此处定义代码。
4. public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
5. public static Thread currentThread():返回对当前正在执行的线程对象的引用。
线程的执行步骤:线程执行,开启了线程,JVM调用其run方法来执行任务。
public class MyThread2 extends Thread { public MyThread2(){ super(); // public Thread() } public MyThread2(String name) { super(name);/// public Thread(String name) } @Override public void run() { //如果没有给线程起名:默认 Thread-x String name = getName(); //获取线程的名字 System.out.println(name +"线程执行起来"); } }
测试类
public class Demo02 { public static void main(String[] args) { //Thread直接创建对象,Thread(),Thread(String name) Thread mt1 = new Thread(); mt1.start(); MyThread2 mt2 = new MyThread2(); mt2.start(); MyThread2 mt3 = new MyThread2("小强"); mt3.start(); //public static Thread currentThread():返回当前方法运行的线程对象 Thread thread = Thread.currentThread(); String name = thread.getName(); System.out.println("main方法执行的线程名字: " + name); //public static void sleep(long mills) for (int i = 0; i < 10; i++) { System.out.println(i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
Thread类API查询:
2.实现Runnable接口
首先我们来看看API是怎么说的
-
public Thread(Runnable target)
:分配一个带有指定目标新的线程对象。 -
public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字。
一般使用步骤:
1)先创建任务【Runnable的对象】
2)借助含有Runnable参数的Thread构造方法,进行创建线程
3)调用线程的start方法开启线程
public class Demo01 { public static void main(String[] args) { Runnable runnable = new MyRunnable(); //1. public Thread(Runnable target):分配一个带有指定目标新的线程对象。 Thread mth1 = new Thread(runnable); mth1.start(); //2. public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。 Thread mth2 = new Thread(runnable, "旺财"); mth2.start(); //3. 匿名内部类实现 Runnable target = new Runnable() { @Override public void run() { Thread thread = Thread.currentThread(); String name = thread.getName(); System.out.println(name+"线程执行run方法【匿名内部类实现】"); } }; new Thread(target).start(); new Thread(target,"旺财").start(); } }
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
多线程代码都是通过调用Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。
总结:
实现Runnable接口比继承Thread类所具有的优势:
-
适合多个相同的程序代码的线程去共享同一个资源。
-
可以避免java中的单继承的局限性。
-
增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
-
线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类。
扩充:在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程。