一个进程在运行时,至少有一个线程在运行。

public class HasOneThreadTest {
public static void main(String[] args) {
//获取当前正在执行的线程
Thread thread = Thread.currentThread();
//获取线程的名字
System.out.println(thread.getName());
}
}

上面代码的运行结果是输出了 main,这个 main 是当前运行的线程和线程名字,和 main 方法的那个 main 没什么关系。

# 通过继承 Thread 创建线程

public class MyThreadTest {
private static class MyThread extends Thread {
//这个run方法里面的代码,就是这个线程具体运行的代码
@Override
public void run() {
System.out.println("当前线程是:" + Thread.currentThread().getName());
}
}

//来测试下线程直接调用run方法和调用start方法的区别
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.run();
myThread.start();
System.out.println("main方法线程名:" + Thread.currentThread().getName());
}
}

运行结果:

run和start

直接调用线程的 run 方法,就跟平常调用类的方法没什么两样,其实还是在主线程中执行的,就不能通过新线程来执行。

启动新线程执行,得用 start() 方法。

# 线程运行具有随机性

public class MyThreadTest {
private static class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
//这个run方法里面的代码,就是这个线程具体运行的代码
@Override
public void run() {
System.out.println("当前线程是:" + Thread.currentThread().getName());
}
}

public static void main(String[] args) {
//创建线程的时候给线程起名字
MyThread t1 = new MyThread("T1");
MyThread t2 = new MyThread("T2");
MyThread t3 = new MyThread("T3");
t1.start();
t2.start();
t3.start();
}
}

运行结果:

线程的随机性

调用 start 方法后:

  1. 调用 start 之后,程序会通知 jvm,告诉 jvm 可以运行了
  2. jvm 异步的调用这个线程对应的 run 方法
  3. jvm 什么时候运行 run 方法,以及先运行哪个线程的 run 方法,这个是没法绝对控制的 (但是可以相对控制)
  4. start 方法的调用顺序,不代表线程的 run 方法的运行顺序。

# 通过实现 Runnable 创建线程

public class MyRunnableThreadTest {
private static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("当前线程是:" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
//Runnable是一个函数式接口,可以用lamdba表达式
Thread thread = new Thread(new MyRunnable());
thread.start();
System.out.println("main线程中执行的代码:" + Thread.currentThread().getName());
}
}

Runnable

但是 Runnable 是一个函数式接口,可以用 Lamdba 表达式:

public class MyRunnableThreadTest {
public static void main(String[] args) {
//Runnable是一个函数式接口,可以用lamdba表达式
Thread thread = new Thread(()->{
System.out.println("当前线程是:" + Thread.currentThread().getName());
}, "runnable");
thread.start();
System.out.println("main end");
}
}

效果:

lamdba表达式

# 通过 Callable 接口创建线程

Java5 开始,Java 提供了 Callable 接口,这个接口怎么看都像是 Runnable 接口的增强版,Callable 接口提供了一个 call () 方法可以作为线程执行体,但 call () 方法比 run () 方法功更强大。call () 方法可以有返回值,call () 方法可以抛出异常。

Callable说明

public class MyCallableTest {
/**
* 用多线程的方式计算10个 1+2+3+...+100的值:50500
*/
public static void main(String[] args) throws ExecutionException, InterruptedException {
//它需要一个方法返回值
Callable<Integer> callable = () -> {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("当前线程是:" + Thread.currentThread().getName() + ",返回值是:" + sum);
return sum;
};
//可以用FutureTask来获取Callable的返回值
Set<FutureTask<Integer>> futureTasks = new HashSet<>();
for (int i = 0; i < 10; i++) {
FutureTask<Integer> futureTask = new FutureTask<>(callable);
futureTasks.add(futureTask);
new Thread(futureTask, "T" + i).start();
}
int result = 0;
for (FutureTask<Integer> futureTask : futureTasks) {
result += futureTask.get();
}
System.out.println("最终的结果是:" + result);
}
}

效果:

Callable

# 三种线程实现方法的区别

1,采用 Thread 方式实现的线程不能继承其他父类,采用 Runnable 和 Callable 接口的可以继承其他父类,但是编程上采用 Thread 的方式可以直接使用 getName () 方法,而采用 Runnable 和 Callable 接口的方式需要先获取 Thread.currentThread ();

2, 采用 Runnable 和 Callable 的方式,可以多个线程公用一个 Target 对象,而采用 Thread 的方式不能,所以非常适合多个相同线程来处理同一份资源的情况

3,如果需要线程有返回值的需要使用 Callable 的方式。