线程其实是程序中的一条执行路径。
线程创建方式1
Java为开发者提供了一个类叫做Thread,此类的对象用来表示线程。创建线程并执行线程的步骤如下
1.定义一个子类继承Thread类,并重写run方法
2.创建Thread的子类对象
3.调用start方法启动线程(启动线程后,会自动执行run方法中的代码)
代码如下
/**
* 开启线程方式1
* 继承Thread类
* 1:创建一个自定义类型 继承Thread
* class MyThread extends Thread
* 2:重写run方法 ---该线程要执行的代码路径
* 3:在需要开启新的线程地方
* 创建线程对象 并且调用 start()方法
*/
public class MyThread extends Thread{
@Override
public void run() { //线程任务
// run代表只要开启 MyThread线程 就会执行run方法代码
for (int i = 0; i < 10; i++) {
System.out.println("在"+Thread.currentThread().getName()+" 线程中执行"+i);
}
}
}
再定义一个测试类,在测试类中创建MyThread线程对象,并启动线程
package com.itheima.d_thread;
public class ThreadTest01 {
/*
Thread 代表线程类
Thread.currentThread() 获取 当前正在执行该代码的线程对象。
Thread对象有个方法
getName() 获取线程的名字
*/
public static void main(String[] args) {
// 当我们点击 RUN ThreadTest01.main的时候 就开启了一个线程 去执行当前main方法里面写的代码 都是由上至下
// 当前执行的时候就是一个线程
System.out.println(Thread.currentThread().getName()+"中执行1");
// 需求 在main线程中开启一个新的线程。
// MyThread myThread = new MyThread();
// myThread.start();
new MyThread().start();
// new MyThread() 既是线程对象 又是 线程任务对象
for (int i = 0; i < 10; i++) {
System.out.println("在"+Thread.currentThread().getName()+"中正在执行:"+i);
}
System.out.println("在"+Thread.currentThread().getName()+"中执行2");
}
}
MyThread和main线程在相互抢夺CPU的执行权(注意:哪一个线程先执行,哪一个线程后执行,目前我们是无法控制的,每次输出结果都会不一样)
最后我们还需要注意一点:不能直接去调用run方法,如果直接调用run方法就不认为是一条线程启动了,而是把Thread当做一个普通对象,此时run方法中的执行的代码会成为主线程的一部分。此时执行结果是这样的。
线程创建方式2
Java为开发者提供了一个Runnable接口,该接口中只有一个run方法,意思就是通过Runnable接口的实现类对象专门来表示线程要执行的任务。
1.先写一个Runnable接口的实现类,重写run方法(这里面就是线程要执行的代码)
2.再创建一个Runnable实现类的对象
3.创建一个Thread对象,把Runnable实现类的对象传递给Thread
4.调用Thread对象的start()方法启动线程(启动后会自动执行Runnable里面的run方法)
代码如下:先准备一个Runnable接口的实现类
/*
1: 创建一个实现类 实现 线程任务接口 Runnable接口 重写 run方法
比如 MyRunnable 重写run 线程的任务
2: 创建 Runable接口实现类对象 MyRunnable对象。
3: 将线程任务对象交给线程对象 创建Thread类对象的同时 传递线程任务对象
new Thread(Runnable实现类对象);
4: 线程对象.start()
*/
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("在"+Thread.currentThread().getName()+"线程中执行"+i);
}
}
}
再写一个测试类,在测试类中创建线程对象,并执行线程
public static void main(String[] args) {
// 当我们点击 RUN ThreadTest01.main的时候 就开启了一个线程 去执行当前main方法里面写的代码 都是由上至下
// 当前执行的时候就是一个线程
System.out.println(Thread.currentThread().getName()+"中执行1");
// 需求 在main线程中开启一个新的线程。
// 2: 创建 Runable接口实现类对象 MyRunnable对象。
MyRunnable mr = new MyRunnable();
// 3: 将线程任务对象交给线程对象 创建Thread类对象的同时 传递线程任务对象
// new Thread(Runnable实现类对象);
// 4: 线程对象.start()
new Thread(mr).start();
for (int i = 0; i < 10; i++) {
System.out.println("在"+Thread.currentThread().getName()+"中正在执行:"+i);
}
System.out.println("在"+Thread.currentThread().getName()+"中执行2");
}
}
运行上面代码,结果如下图所示**(注意:没有出现下面交替执行的效果,也是正常的)**
主线程main输出 ===》1
主线程main输出 ===》2
主线程main输出 ===》3
子线程输出 ===》1
子线程输出 ===》2
子线程输出 ===》3
子线程输出 ===》4
子线程输出 ===》5
主线程main输出 ===》4
主线程main输出 ===》5
线程创建方式2—匿名内部类
第二种线程的创建方式,需要写一个Runnable接口的实现类,然后再把Runnable实现类的对象传递给Thread对象。
现在我不想写Runnable实现类,于是可以直接创建Runnable接口的匿名内部类对象,传递给Thread对象。
代码如下
public class ThreadTest03 {
/*
Thread 代表线程类
Thread.currentThread() 获取 当前正在执行该代码的线程对象。
Thread对象有个方法
getName() 获取线程的名字
*/
public static void main(String[] args) {
// 当我们点击 RUN ThreadTest01.main的时候 就开启了一个线程 去执行当前main方法里面写的代码 都是由上至下
// 当前执行的时候就是一个线程
System.out.println(Thread.currentThread().getName()+"中执行1");
// 需求 在main线程中开启一个新的线程。
// new Thread(Runnable的实现类对象).start();
// new Thread(Runnable匿名内部类对象).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("在"+Thread.currentThread().getName()+"中正在执行:"+i);
}
}
}).start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("在"+Thread.currentThread().getName()+"中正在执行:"+i);
}
}).start();
for (int i = 0; i < 10; i++) {
System.out.println("在"+Thread.currentThread().getName()+"中正在执行:"+i);
}
System.out.println("在"+Thread.currentThread().getName()+"中执行2");
}
}
线程的创建方式3
-
假设线程执行完毕之后有一些数据需要返回,前面两种方式重写的run方法均没有返回结果。
public void run(){ ...线程执行的代码... }
-
JDK5提供了Callable接口和FutureTask类来创建线程,它最大的优点就是有返回值。
在Callable接口中有一个call方法,重写call方法就是线程要执行的代码,它是有返回值的
public T call(){ ...线程执行的代码... return 结果; }
第三种创建线程的方式,步骤如下
1.先定义一个Callable接口的实现类,重写call方法
2.创建Callable实现类的对象
3.创建FutureTask类的对象,将Callable对象传递给FutureTask
4.创建Thread对象,将Future对象传递给Thread
5.调用Thread的start()方法启动线程(启动后会自动执行call方法)
等call()方法执行完之后,会自动将返回值结果封装到FutrueTask对象中
6.调用FutrueTask对的get()方法获取返回结果
代码如下:先准备一个Callable接口的实现类
/**
* 使用Callable接口 形式来玩新的线程
* 1:创建一个Callable接口实现类 泛型表示返回值的类型
* 2: 重写方法 任务 求绝对值
* 3: 在需要创建新线程的地方 先创建一个Callable实现类对象
* 4: 创建 处理用于接收线程任务返回值类对象 FutureTask 传递线程任务
* 5:创建线程对象 传入 task对象 .start()启动新的线程。
* 6: 结果 找 task.get()处理
*/
public class MyCallable implements Callable<Integer> {
private Integer number;//定义了成员变量
public MyCallable(Integer number){//怎么把接收到number传递到 下面call方法中
this.number = number;//传过来的值 给了 成员变量
}
// 求一个数的绝对值
@Override
public Integer call() throws Exception {
System.out.println("当前在:"+Thread.currentThread().getName()+"完成绝对值的获取");
return Math.abs(number); //使用到了成员变量
}
}
再定义一个测试类,在测试类中创建线程并启动线程,还要获取返回结果
public class ThreadTest04 {
/*
Thread 代表线程类
Thread.currentThread() 获取 当前正在执行该代码的线程对象。
Thread对象有个方法
getName() 获取线程的名字
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 当我们点击 RUN ThreadTest01.main的时候 就开启了一个线程 去执行当前main方法里面写的代码 都是由上至下
// 当前执行的时候就是一个线程
System.out.println(Thread.currentThread().getName()+"中执行1");
// 需求 在main线程中开启一个新的线程。
// 创建 Callable实现类对象
MyCallable mc = new MyCallable(-10);
//创建 处理用于接收线程任务返回值类对象 FutureTask 传递线程任务
FutureTask<Integer> task = new FutureTask<>(mc);
// 创建线程对象 传入 处理返回值的线程任务
new Thread(task).start();//线程对象
// 返回值怎么处理 ? tack处理返回值
System.out.println("新线程的返回值是:"+task.get());
System.out.println("在"+Thread.currentThread().getName()+"中执行2");
}
}