目录
一. 多线程概述
1.进程与线程
进程:进程是指一块内存中运行的应用程序(软件),现在有部分软件,一个软件就有多个进程,但是在学习过程中为了好理解,可以把一个进程当作一个独立工作的软件来理解。每个进程都有自己独立的空间(栈和堆),除非通过一些技术手段,否则两个进程之间没办法实现相互通信。
线程:线程是指一个程序中执行的任务,一个程序(进程)内有多个线程,每个线程都有自己独立的栈,共享堆内存,当一个进程中没有一个线程执行了,代表软件关闭,也就是说一个进程在执行,那么该进程中至少有一个线程在运行。
多线程:多线程就是指一个一个程序可以执行多个任务。
补充:多线程实际上是提高了运行效率,而不是速度,很多人在理解多线程时,都觉得多个任务执行是加快了速度,其实不然,原因在于:Java多线程的线程调度采用的是抢占式调度机制。
解释:你的电脑是六核处理器的,则有12个大脑(cpu),则意味着你的电脑实际上只能在同一时刻执行12个任务,然而电脑上执行的任务是远远大于这个数量的,所以每个任务就会抢占时间偏,只是电脑运算速度很快,抢占时间偏时间很短几乎可以忽略,然后造成的所有任务同时进行的假象。实际上是优先级越高的抢占到的几率越大,优先级相同则抢到几率一样。
一个小问题:1000个人操作服务器是同时执行速度快,还是排队执行快?这个问题会产生一个误区,这个误区就是刚才补充的问题。会有人觉得同时执行快,实际上是排队快。因为1000个人同时操作那么抢占时间偏那个过程也会花一点时间,因为要来回切换抢占,虽然可以忽略,但是在量很大的时候,也会占一点时间,所有实际上是排队,一个线程给它执行完快,但是效率低。
2.同步与异步
同步:排队执行,效率低但安全
异步:同时执行,效率高但不安全
3.并发与并行
并发:同一时间段执行
并行:同一时刻执行
误区:服务器有5000个任务并行,大脑才多少个,就5000个同时执行了?所以不对
二. 实现多线程
1.通过继承Thread实现多线程
package com.java.test;
public class Test {
public static void main(String[] args) {
//创建子线程对象
MyThread myThread = new MyThread();
//开启子线程
myThread.start();
//主线程执行的任务
for (int i=0;i<3;i++){
System.out.println("疑是地上霜"+i);
}
}
}
class MyThread extends Thread{
@Override
//通过重写Thread类中的run方法来实现子线程要执行的任务
public void run() {
for (int i=0;i<3;i++){
System.out.println("床前明月光"+i);
}
}
}
运行结果:
床前明月光0
床前明月光1
疑是地上霜0
床前明月光2
疑是地上霜1
疑是地上霜2
小贴士:每次运行输出的结果顺序都不同,因为每次抢占时间偏不同。
2.通过实现Runnable实现多线程
package com.java.test;
public class Test {
public static void main(String[] args) {
//创建任务对象
MyRunnable m = new MyRunnable();
//创建子线程,通过传入Runnable实例对象完成任务,没有重写Thread中的run方法
Thread t = new Thread(m);
//开启子线程
t.start();
//主线程任务
for (int i=0;i<3;i++){
System.out.println("疑是地上霜"+i);
}
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<3;i++){
System.out.println("床前明月光"+i);
}
}
}
运行结果:
床前明月光0
床前明月光1
疑是地上霜0
疑是地上霜1
疑是地上霜2
床前明月光2
小贴士:通过实现Runnable接口实现多线程达到了任务与线程分离,并且接口可以多实现,类不能多继承,所以使用Runnable有时候更方便,也不是不推荐使用Thread,根据需求进行操作。
3.通过实现Callable实现多线程
Runnable与Callable相同点:都是接口,都是创建Thread开启线程,
Runnable与Callable不同点:Callable中的call方法可以抛异常,使用Callable还可以通过创建
FutureTask 调用get方法获取返回值,使主线程等待子线程执行完毕再执行。
package com.java.test;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test5 {
public static void main(String[] args) {
//创建Callable
Callable<Integer> c = new MyCallable();
//创建FutureTask
FutureTask<Integer> f = new FutureTask<>(c);
//开启子线程
new Thread(f).start();
try {
//调用get方法使主线程阻塞,等待子线程执行完毕,主线程才执行
Integer integer = f.get();
System.out.println("子线程执行完毕,获得返回值:"+integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//主线程任务
for (int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":疑是地上霜"+i);
}
}
}
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
for (int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+":床前明月光"+i);
}
return 100;
}
}
运行结果:
Thread-0:床前明月光0
Thread-0:床前明月光1
Thread-0:床前明月光2
Thread-0:床前明月光3
Thread-0:床前明月光4
子线程执行完毕,获得返回值:100
main:疑是地上霜0
main:疑是地上霜1
main:疑是地上霜2
main:疑是地上霜3
main:疑是地上霜4
小贴士: 如果中间不调用f.get方法,则子线程和主线程还是抢占时间偏执行
三. Thread类常用方法
1.sleep(休眠)
public static void sleep(long millis) throws InterruptedException
/*参数
millis - 以毫秒为单位的睡眠时间长度
异常
IllegalArgumentException - 如果 millis值为负数
InterruptedException - 如果有任何线程中断了当前线程。 抛出此异常时,将清除当前线程的中断状态 */
public static void sleep(long millis, int nanos) throws InterruptedException
/*参数
millis - 以毫秒为单位的睡眠时间长度
nanos - 0-999999睡觉的额外纳秒
异常
IllegalArgumentException - 如果 millis值为负数,或者值 nanos不在 0-999999范围内
InterruptedException - 如果有任何线程中断了当前线程。 抛出此异常时,将清除当前线程的中断状态*/
package com.java.test;
public class Test {
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<3;i++){
System.out.println("床前明月光"+i);
Thread.sleep(1000);
}
}
}
运行结果:
床前明月光0
床前明月光1
床前明月光2
每隔1秒打印一次
2.currentThread调用当前线程的对象
public static Thread currentThread()
/*返回对当前正在执行的线程对象的引用*/
package com.java.test;
public class Test {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"床前明月光"+i);
}
}
}.start();
for (int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+"床前明月光"+i);
}
}
}
运行结果:
Thread-0床前明月光0
main床前明月光0
Thread-0床前明月光1
main床前明月光1
Thread-0床前明月光2
main床前明月光2
3.interrupt标记线程中断
public void interrupt()
/*中断此线程。 */
package com.java.test;
public class Test {
public static void main(String[] args) {
//创建子线程
MyThread m = new MyThread();
//开启子线程
m.start();
for (int i=0;i<3;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"床前明月光"+i);
}
//子线程打上标记
m.interrupt();
}
}
class MyThread extends Thr