在完成对C语言的学习后,我最近开始了对C++和Java的学习,目前跟着视频学习了一些语法,也跟着敲了一些代码,有了一定的掌握程度。现在将跟着视频做的笔记进行整理。本篇博客是整理Java知识点的第二十八篇博客。
本篇博客介绍了Java的多线程初级内容。
本系列博客所有Java代码都使用IntelliJ IDEA编译运行,版本为2022.1。所用JDK版本为JDK11。
目录
多线程初级
进程与线程
进程是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有自己的内存空间和系统资源。
线程是进程中的单个顺序控制流,是一条执行路径。一个进程如果只有一条执行路径,就是单线程。一个进程如果有多条执行路径,就是多线程。
继承Thread类的方式实现多线程
要通过继承Thread类的方式实现多线程,要先定义一个类继承Thread类,然后在这个类中重写run方法,再创建此类对象,最后启动线程。
run方法封装被线程执行的代码,直接调用run方法相当于普通方法调用。
start方法启动线程,然后会由JVM调用此线程的run方法。
public class MyThread extends Thread{
public void run(){
int i;
for(i = 1; i <= 100; i += 1) {
System.out.println(i);
}
}
}
MyThread类继承了Thread类,重写run方法,run方法输出1至100。
public class MyThreadtest1 {
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
my1.start();
my2.start();
}
}
程序创建了两个MyThread类对象,然后调用start方法。
程序实现了多线程,由于输出过长,此处不再展示。
设置和获取线程名称
void setName(String name)将此线程的名称改为参数name。
String getName()返回此线程名称。
通过构造方法也可以设置线程名称。
public static Thread currentThread()返回当前正在执行的线程对象的引用。
public class MyThread extends Thread{
public void run(){
int i;
for(i = 1; i <= 100; i += 1) {
System.out.println(getName() + " " + i);
}
}
}
MyThread类继承了Thread类,重写run方法,run方法输出1至100,每次都会输出线程名称。
public class MyThreadtest2 {
public static void main(String[] args){
System.out.println(Thread.currentThread());
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
my1.setName("A");
my2.setName("B");
my1.start();
my2.start();
}
}
程序首先输出当前线程名称,然后创建了两个MyThread类对象并调用start方法。
下面是程序开始的一部分输出:
Thread[main,5,main]
B 1
A 1
B 2
A 2
B 3
A 3
B 4
A 4
B 5
A 5
线程优先级
线程有两种调度模型。
分时调度模型所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片。
抢占式调度模型优先让优先级高的线程使用CPU,如果线程优先级相同会随机选择一个,优先级高的线程获取的CPU的时间片相对多一些。
Java使用抢占式调度模型。
如果计算机只有一个CPU,那么CPU在某个时刻只能执行一条指令。线程只有得到CPU时间片才能执行指令。多线程执行有随机性,因为谁抢到CPU的使用权是不一定的。
public final int getPriority()返回此线程优先级。
public final void setPriority(int newPriority)更改此线程优先级。
线程默认优先级是5,优先级的范围是1至10。
线程优先级高仅仅表示线程获取CPU时间片的几率高。
public class MyThreadtest3 {
public static void main(String[] args){
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
MyThread my3 = new MyThread();
my1.setName("A");
my2.setName("B");
my3.setName("C");
my1.setPriority(6);
my2.setPriority(7);
my3.setPriority(8);
my1.start();
my2.start();
my3.start();
}
}
MyThread类见上文。程序创建了三个MyThread类对象,然后设置姓名和优先权,随后启动多线程。
下面是程序一次输出的一部分:
C 1
B 1
A 1
B 2
C 2
B 3
A 2
B 4
C 3
B 5
A 3
B 6
C 4
B 7
A 4
B 8
线程控制
static void sleep(long millis)让当前执行的线程暂停执行millis毫秒。
void join()表示这个线程结束后再执行其他线程。
void setDaemon(boolean on)将此线程记为守护线程,如果运行的线程都是守护线程,那么程序结束。
线程生命周期
首先创建一个线程对象,然后用start方法启动,此时有执行资格无执行权,如果抢到CPU执行权就运行,如果运行时其他线程抢走CPU执行权就失去执行权,等待抢到CPU执行权。如果用sleep或其他阻塞方法就失去执行资格和执行权,等待sleep方法时间到或者阻塞方式结束,随后获得执行资格,等待抢到CPU控制权。线程执行完毕后就变成垃圾。
实现Runnable接口的方式实现多线程
通过实现Runnable接口的方式实现多线程,需要定义一个类实现Runnable接口,随后在这个类中重写run方法,然后创建这个类的对象。再创建一个Thread类对象,把这个类的对象作为构造方法的参数,再启动线程。
多线程可以通过继承Thread类或实现Runnable接口的方式实现。实现Runnable接口避免Java单继承的局限性,适合多个相同程序的代码处理同一个资源的情况,把线程和程序的代码和数据有效分离。
public class MyRunnable implements Runnable{
public void run(){
int i;
for(i = 1; i <= 100;i += 1){
System.out.println(Thread.currentThread() + " " + i);
}
}
}
MyRunnable类实现了Runnable接口,重写了run方法。
public class MyRunnabletest {
public static void main(String[] args){
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.start();
t2.start();
}
}
程序创建了一个MyRunnable类对象mr,随后通过Thread创建了两个对象,构造方法参数为mr。然后启动多线程。
程序的输出是(只展示前十五行结果):
Thread[Thread-1,5,main] 1
Thread[Thread-1,5,main] 2
Thread[Thread-0,5,main] 1
Thread[Thread-1,5,main] 3
Thread[Thread-0,5,main] 2
Thread[Thread-1,5,main] 4
Thread[Thread-0,5,main] 3
Thread[Thread-1,5,main] 5
Thread[Thread-0,5,main] 4
Thread[Thread-1,5,main] 6
Thread[Thread-0,5,main] 5
Thread[Thread-1,5,main] 7
Thread[Thread-0,5,main] 6
Thread[Thread-1,5,main] 8
Thread[Thread-0,5,main] 7