多线程的创建有以下三种方式:
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法
- 匿名内部类
其实还可以使用JDK1.5之后的Callable类和线程池的方式创建,但是该系列只是Java多线程入门,而且我也对线程池的使用不是很了解,所以就没有写它们了。但是好像上面的这些方式都用的不多,都是使用线程池创建线程。(哈哈,只能怪自己太菜,菜是原罪啊,还得继续加油,欧力给)
1、继承Thread类
使用Thread类创建线程的步骤:
-
创建一个继承自Thread类的子类。
- 重写Thread类的run()方。
- 创建Thread类的子类对象。
- 通过子类对象调用start()方法。
package com.thr; /** * @author Administrator * @date 2020-03-13 * @desc 继承Thread类 */ //1、创建一个继承自Thread类的子类 class MyThread extends Thread{ //2、重写Thread类的run()方法 @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } public class ThreadDemo { public static void main(String[] args) { //3、创建Thread类的子类对象 MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); //4、通过子类对象调用start()方法 t1.start(); t2.start(); } }
运行结果如下:
从上面的运行结果可以看出:线程是一个子任务,创建的线程是交替执行的。这是线程调度的结果,Java的线程调度策略是优先级抢占式调度,首先让优先级高的线程大概率能够使用CPU资源,如果线程的优先级相同,那么CPU会随机调度(线程随机性),上面的代码都是默认的优先级,创建的两个线程同时在抢占CPU的资源,那么谁能抢占到的CPU资源就执行。所以输出的结果是随机的,我们是不能干涉的。
注意:不要将run()和start()这两者给搞混了。
run()和start()方法区别:
- start():首先启动当前线程,然后再由JVM去调用该线程的run()方法。
- run():仅仅是封装被线程执行的代码,直接调用是普通方法,并不会启动线程。
还有需要注意一点的是:如果该线程已经调用了start()方法,则不能再次调用该方法了,否则会抛出IllegalThreadStateException异常。解决办法就是重新创建一个Thread子类的实例调用start()方法。
2、实现Runnable接口
使用Runnable接口创建线程的步骤:
-
创建一个实现Runnable接口的类。
- 实现类去实现Runnable接口中的抽象方法run()。
- 创建实现类的对象。
- 创建一个Thread类,将实现类的对象传入到Thread类的构造器中。
- 通过Thread类的对象调用start()方法。
package com.thr; /** * @author Administrator * @date 2020-03-13 * @desc 实现Runnable接口 */ //1、创建一个实现了Runnable接口的类 class MyThread implements Runnable{ //2、实现Runnable接口的抽象run()方法 @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } public class RunnableDemo { public static void main(String[] args) { //3、创建实现类对象 MyThread m = new MyThread(); //4、创建一个Thread类,将实现类的对象传入到Thread类的构造器中。 Thread t1 = new Thread(m); Thread t2 = new Thread(m); //5、通过Thread类的对象调用start()方法。 t1.start(); t2.start(); } }
既然有这两种方式实现对线程,那么我们使用哪一种呢???
答:一般使用的是实现Runnable接口方式开发多线程,因为Java只能单继承却可以实现多个接口。
3、匿名内部类
Thread匿名内部类
package com.thr; /** * @author Administrator * @date 2020-03-13 * @desc Thread类的匿名内部方式 */ public class ThreadDemo { public static void main(String[] args) { //创建一个打印1000以内的所有偶数的线程 new Thread(){ @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } }.start(); //创建一个打印1000以内的所有奇数的线程 new Thread(){ @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 != 0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } }.start(); } }
Runnable作为参数传入Thread类的匿名内部方式。
package com.thr; /** * @author Administrator * @date 2020-03-13 * @desc Runnable作为参数传入Thread类的匿名内部方式 */ public class RunnableDemo1 { public static void main(String[] args) { //创建一个打印1000以内的所有偶数的线程 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } }).start(); //创建一个打印1000以内的所有奇数的线程 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 != 0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } }).start(); } }