线程
1什么是线程
线程总体分两类:用户线程和守护线程。
我们的main方法就是跑在线程中的,其实我们启动的每一个线程都可以看作是一个新的main线程,其中守护线程包含我们的GC线程。
2线程与进程
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。JVM会给它在栈内存空间中开辟新的空间来执行线程。
简单说来就是,一个进程可能包含多个线程
讲到这里,我们再来认识一下并行和并发
并行,指的是多个程序一起执行,需要依靠多核心处理器,处理器就相当于我们人的大脑,我们没法做到一心二用就是因为我们只有一个大脑,我们在边听歌边写作业的时候,其实并不是同时进行的,我们在同一时刻只能执行一件事情。
并发,就是指多个程序交替执行,我们一边写作业一边听歌就是这种情况,单核处理器就会出现并发的问题,我们不能再一个时刻干很多事情,只能一个一个完成,一个一个推进,每件事情分配的时间完全取决于我们的cpu。
3多线程的实现
我们实现多线程的方法主要有三个
1继承Thread类,重写run方法
package com.fq.onclass526;
public class Example_1 extends Thread{
String threadID;
public Example_1(String threadID) {
this.threadID = threadID;
}
public void run() {
System.out.println("Thread started:" + this.threadID);
for (int i = 0; i < 6; i++) {
System.out.println(threadID + " i = " + (i + 1) + "\t");
}
System.out.println("Thread stopped:" + this.threadID);
}
}
package com.fq.onclass526;
public class ThreadTest1 {
public static void main(String[] args) {
System.out.println("Starting ThreadTest1");
Example_1 t1 = new Example_1("Thread1");
t1.start();
Example_1 t2 = new Example_1("Thread2");
t2.start();
Example_1 t3 = new Example_1("Thread3");
t3.start();
System.out.println("ThreadTest1 is done");
}
}
Starting ThreadTest1
ThreadTest1 is done
Thread started:Thread1
Thread started:Thread3
Thread started:Thread2
Thread3 i = 1
Thread1 i = 1
Thread3 i = 2
Thread3 i = 3
Thread3 i = 4
Thread3 i = 5
Thread2 i = 1
Thread2 i = 2
Thread2 i = 3
Thread3 i = 6
Thread1 i = 2
Thread stopped:Thread3
Thread2 i = 4
Thread1 i = 3
Thread2 i = 5
Thread1 i = 4
Thread2 i = 6
Thread1 i = 5
Thread stopped:Thread2
Thread1 i = 6
Thread stopped:Thread1
Process finished with exit code 0
奇怪不,明明说程序顺序执行,可是为什么
Starting ThreadTest1
ThreadTest1 is done
会打印在一起呢,就是因为多线程的原因,cpu给main线程分配到的时间刚好可以先把main方法执行完,之后再给其他线程分配空间。
在这段代码中其他线程开始的时间分配是怎么样的呢?
我们在执行前并不清楚,只有在结果出来以后才明白
他们的启动的顺序应该是Thread1—>Thread3—>Thread2
这一种方法有一些不足之处,大家想到了吗?
那就是不能多继承,比如我想同时继承Thread类,我又想它继承LinkedList类,那么就无法实现了
2实现Runnable接口,重写run方法
这种方法我们最常使用,但是为什么实现Runnable接口可以实现呢?他只有run方法啊,不能启动线程,只能像lambda表达式一样直接调用run啊,那还是在main线程里面
我们看看Thread的构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
原来如此,我们可以用一个实现了Runnable接口的类来构造Thread,用Thread的实例对象调用start方法
package com.fq.onclass526;
import java.util.ArrayList;
import java.util.concurrent.Callable;
public class Example11_2 implements Runnable{
String threadId;
public Example11_2(String threadId) {
this.threadId = threadId;
}
@Override
public void run() {
System.out.print("\n Thread started:"+this.threadId);
for(int i = 0;i<6;i++)
System.out.print(" i = "+(i+1)+"\t");
System.out.print("\n Thread stopped:"+this.threadId);
}
}
public class ThreadTest2 {
public static void main(String[] args) {
System.out.println("Starting ThreadTest1");
Runnable r1 = new Example11_2("thread1");
Thread t1 = new Thread(r1);
t1.start();
Runnable r2 = new Example11_2("thread2");
Thread t2 = new Thread(r2);
t2.start();
Runnable r3 = new Example11_2("thread3");
Thread t3 = new Thread(r3);
t3.start();
System.out.println("ThreadTest2 is done");
}
}
Starting ThreadTest1
ThreadTest2 is done
Thread started:thread1 i = 1
Thread started:thread3
Thread started:thread2 i = 1 i = 2 i = 2 i = 1 i = 3 i = 3 i = 4 i = 2 i = 3 i = 4 i = 5 i = 6 i = 5 i = 4 i = 6
Thread stopped:thread2
Thread stopped:thread3 i = 5 i = 6
Thread stopped:thread1
3实现Callable接口,重写call方法
Runnable 和 Callable 接口的区别
其中的方法不同,一个是run,一个是call
call方法有返回值
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Callable任务借助FutureTask运行
Runnable任务借助Thread运行