一、基本概念
1.什么是线程?
线程就是,操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。简单理解就是:应用软件中互相独立,可以同时运行的功能
2.什么是多线程?
有了多线程,我们就可以让程序同时做多件事情
3.多线程的作用?
提高效率
4.线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程
比如:软件中的耗时操作、所有的聊天软件、所有的服务器
二、并发和并行的概念
1.什么是并发?
并发就是,同一时刻,有多个指令在单个CPU上交替执行。
2.什么是并行?
并行就是,同一时刻,有多个指令在多个CPU上同时执行
3.电脑不是只有一个CPU么,这个多个CPU同时执行的并行究竟是什么?
其实,CPU在市面有很多类型如下
比如2核4线程的CPU,就可以同时运行4个线程的任务。
三、多线程的实现方式(3种)
1.继承Thread类的方式进行实现
用法:
1.定义一个类继承Thread类
2.这个类重写run方法
3.在main方法里面创建定义的类的对象
4.通过该对象的.start()方法启动线程
示例代码
public class ThreadDemo1 { public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.setName("线程1"); myThread2.setName("线程2"); myThread1.start(); myThread2.start(); } } class MyThread extends Thread{ @Override public void run(){ for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } }
上面的两个线程的代码run方法是同时执行的,并不会等一个线程的循环走完。
注意:线程类开启后执行的是run方法的代码
2.实现Runnable接口的方式进行实现
用法:
1.自己定义一个类实现Runnable接口
2.重写里面的run方法
3.在main方法创建自己的类的对象
4.将定义的类传递给Thread构造方法创建一个Thread类的对象,并开启线程
示例
public class ThreadDemo2 { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread1.setName("线程1"); thread2.setName("线程2"); thread2.start(); thread1.start(); } } class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
3.利用Callable接口和Future接口方式实现
前面两种实现方式,run方法没有返回值,不知道线程实现的结果,现在这个第三种方法是有返回值的。
用法:
1.创建一个类MyCallable实现callable接口
2.重写call(是有返回值的,表示多线程运行的结果)
3.创建MyCallable的对象(表示多线程要执行的任务)
4.传递MyCallable对象为参数创建FutureTask的对象(作用管理多线程运行的结果)
5.传递FutureTask对象为参数创建Thread类的对象,并启动(表示线程)
示例
public class ThreadDemo3 { public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable mc = new MyCallable(); FutureTask<Integer> ft = new FutureTask<>(mc); Thread t = new Thread(ft); t.start(); System.out.println(ft.get()); } } class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i <= 100; i++) { sum += i; } return sum; } }
结果输出5050
4.三种方式的比较
四、Thread类的常用成员方法
1.七个方法
2.前四个细节
String getName()
void setName(String name)
细节:
1. 果我们没有给线程设置名字,线程也是有默认的名字的
格式:Thread-X(X序号,从0开始的)
2.如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
static Thread currentThread()
细节:
当JVM虚拟机启动之后,会自动的启动多条线程
其中有一条线程就叫做main线程
他的作用就是去调用main方法,并执行里面的代码
在以前,我们写的所有的代码,其实都是运行在main线程当中
static void sleep(long time)
细节:
1.哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
2.方法的参数:就表示睡眠的时间,单位毫秒
1秒 = 1000毫秒
3.时间到了之后,线程会自动的醒来,继续执行下面的其他代码
3.线程的优先级
(1)线程调度
抢占式调度:各条线程执行的顺序和时间是不确定的(随机)
非抢占式调度:各条线程执行的顺序是轮流执行和执行时间是差不多的
java中是选取第一种抢占式调度
(2)优先级(1~10)
抢占式调度就要涉及到线程的优先级越大,执行的顺序越前
于是可以通过Thread类的上面的两个成员方法来设置和获取线程的优先级
没有设置默认就是5
注意:这里的优先级是指优先级大的线程先执行的概率比较大,而不是百分百,比如说有两个一样的方法的线程,优先大的线程是有概率计较大的先执行完,但还是有小概率执行慢
4.守护线程
当一个线程使用Thread类的etDaemon(boolean on)时,这个线程变成守护线程。
细节:
这个线程也可以叫做“备胎”线程,它将其他线程视为“女神”线程,当其他线程结束的时候,这个守护线程就会陆续结束,觉得自己没必要存在了,这就会可能导致守护线程的代码没有全部执行完。
应用场景
上面聊天的场景,两个人聊天开两个线程,一个聊天,一个传输文件,如果聊天线程关闭了,就没有传输文件的必要了,于是将传输文件的线程设置为守护线程。
5.出让/礼让线程
在当前线程的工作不重要时,将CPU资源让位给其他线程,通过使用Thread类的
yield()
方法来将当前资源让位给其他同优先级线程publ