一、概述
1、进程:
正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和资源。
2、线程:
是进程的单个顺序控制流,或者说就是一个单独执行的路径
一个进程如果只有一条执行路径,称之为单线程
一个进程如果有多条执行路径,称之为多线程
线程是包含在进程中。
3、三个关键词:
(1)串行,指的是一个程序中所有的任务都是按照先后顺序执行的,在前一个任务还没有处理完的情况下,是不会进行处理下一个任务的
举例:理发店只有一个理发师,很多人去理发,先等前面的人理完发,再轮到后面的人。
(2)并行,指的是将任务分给不同的处理器去处理,每一个处理器中再进行串行处理。
举例:火车站上有很多个窗口,多个窗口同时卖票,但是针对于一个窗口来说,一个人的一个人的去卖票
(3)并发,实质上是一种现象,并发需要处理器的支持,比如在出库一个任务的时候操作系统可以进行调用再处理其他的任务,不论串行还是并行
都需要操作系统的支持并发。假设喝水是一个任务,每个火车站售票员,再售票的同时也能喝水,这就表示支持并发。
4、jvm启动时是多线程,main线程(主线程)和垃圾回收线程
二、多线程实现方案
1、创建线程的第一种方式:继承Thread类
(1)步骤
创建一个自定义类继承Thread类:假设创建的类名叫MyThread1
这个类要重写Thread类中的run方法
当线程启动之后,执行的代码逻辑仅是run()方法的代码逻辑
根据这个类创建线程对象:MyThread1 myThread1 = new MyThread1();为了实现多线程,至少创建2个及两个以上的线程对象
启动线程:如果调用的是run()方法则和普通方法实现没有区别,需要用到start()方法去实现
调用start()与调用run()的区别
run()方法中仅仅是封装了被线程执行的代码,但是呢,直接调用run()与调用普通的方法方式没有任何区别
start()方法的调用,首先单独启动了一个线程,然后再由JVM去调用该线程类中的run()方法
(2)实现
class Student extends Thread {
@Override
public void run() {
for(int i=0;i<200;i++){
System.out.println(i);
}
}
}
public class Test1 {
public static void main(String[] args) {
Student s1=new Student();
Student s2=new Student();
s1.start();
s2.start();
}
}
(3)Thread类中的方法:public final String getName()返回此线程的名称
public final void setName(String name)将此线程的名称更改为等于参数name 。
public static Thread currentThread()返回对当前正在执行的线程对象的引用,在加上getName()获取线程的名称。
class Student extends Thread {
public Student() {
}
public Student(String name) {//通过构造方法给线程起名字
super(name);
}
@Override
public void run() {
for(int i=0;i<200;i++){
System.out.println(getName()+":"+i);//可以直接调用父类的方法
}
}
}
public class Test1 {
public static void main(String[] args) {
Student s1=new Student();
Student s2=new Student();
s1.setName("张cell");
s2.setName("张see");
s1.start();
s2.start();
}
}
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
结果
main
(4)线程优先级
线程的默认优先级是5
线程优先级的范围是1-10
线程优先级高的可能比低的先获取到,线程优先级高仅仅表示的获取CPU时间片的机率会高一些,但是呢,并不是绝对会一直获取到
获取线程优先级的方法:
public final int getPriority()返回此线程的优先级。
设置线程优先级的方法:
public final void setPriority(int newPriority)更改此线程的优先级。
Student s1 = new Student();
Student s2 = new Student();
s1.setPriority(1);
s2.setPriority(4);
System.out.println(s1.getPriority());
System.out.println(s2.getPriority());
结果是1和4
(5)线程有两种调度模型:
分时调度模型 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
所以综上Java使用的是抢占式调度模型。
(6)线程控制
线程休眠 public static void sleep(long millis):加在对象类中,线程每过millis时间(单位是毫秒)在执行,需要try...catch..
public class Student extends Thread {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println(getName() + ":" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程加入:public final void join():join的调用必须在start之后,需要try...catch..
线程对象调用join的目的是让当前线程先执行完毕,完毕后其他的线程再进行执行换句话说,就是其他的线程等待调用join的线程执行完毕后再执行。
Student s1=new Student();
Student s2=new Student();
s1.setName("张小庆");
s2.setName("张大庆");
s1.start();
try {
s1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
s2.start();
加入join后,s1运行完毕后才会运行s2线程
礼让线程:public static void yield():加在对象类run()方法中,和sleep()方法一样使用
暂停当前正在执行的线程对象,并执行其他线程,它的作用是为了让多个线程之间运行的时候看起来更加和谐,但是呢,并不能保证多个线程一人一次。
后台线程:(守护线程)public final void setDaemon(boolean on)
Java中有两类线程:用户线程,守护线程
用户线程:在学习多线程之前,运行起来的一个个的线程都是用户线程
守护线程:所谓守护线程,指的是程序在运行的时候,在后台提供了一个通用的服务线程。比如说垃圾回收线程,他就是一个守护线程
并且这种线程并不是一定存在的,所以反过来说,只要程序存在守护线程,程序就不会终止。
注意:
1、守护线程必须在启动之前进行设置
2、当运行的程序只有一个线程的时候并且这个线程是守护线程的时候,Java虚拟机退出(程序停止)
Student s1=new Student();
Student s2=new Student();
s1.setName("张小庆");
s2.setName("张大庆");
s2.setDaemon(true);
s1.start();
s2.start();
开始是s1和s2是抢占运行的,当s1运行完毕后,线程中只剩下守护线程s2后,jvm会停止运行,
所以s2在s1运行结束后,继续运行了一会儿就停止了,并没有运行完毕
中断线程:public void interrupt():
class MyStopThread extends Thread {
@Override
public void run() {
System.out.println("开始执行时间:" + new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束时间:" + new Date());
}
}
public class ThreadStopDemo {
public static void main(String[] args) {
MyStopThread t1 = new MyStopThread();
t1.setName("小花");
t1.start();
t1.interrupt(); //打断睡眠,run方法后面的代码继续执行,执行完后,抛出异常
}
}
结果
开始执行时间:Sun Feb 20 10:21:33 CST 2022
结束时间:Sun Feb 20 10:21:33 CST 2022
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.shujia.yh.pratice.MyStopThread.run(Test1.java:10)
(7)总结
2、多线程的实现方案二:实现Runnable接口
(1)步骤
自定义一个类实现Runnable接口
实现run()方法
创建自定义类对象
创建Thread线程对象,将自定义的对象作为参数传递到构造方法中
(2)实现
class Ra implements Runnable {
@Override
public void run() {
for(int i=0;i<200;i++){
//由于Runnable接口中没有getName()方法,所以这里无法使用获取线程对象名字
//间接调用,我们可以先获取当前线程的对象,然后再调用Thread类中getName()方法
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class Test1 {
public static void main(String[] args) {
Ra r1=new Ra();
Thread t1=new Thread(r1);
Thread t2=new Thread(r1);
t1.setName("张小青");
t2.setName("张大庆");
t1.start();
t2.start();
}
}