一、概念
1.线程与进程
进程:在内存中执行的应用程序(一个进程中至少有一个线程,还可以有多个线程)
线程:是进程中的最小执行单元(一个功能需要一条线程执行)
在CPU和内存之间为每一个功能开辟对应的通道,方便CPU去内存中提取代码做计算,这个通道就是线程。
一个线程干一件事,同时干多件事就可以提高CPU的利用率。
2.并发和并行
并行:同一时刻,有多个事情在多个CPU上同时执行。
并发:同一时刻,有多个事情在单个CPU上交替执行。
3.CPU调度
分时调度:指的是让所有线程轮流获取CPU的使用权,并且平均分配每个线程占用CPU 的时间。
抢占式调度:多个线程轮流抢占CPU使用权,哪个线程先抢到,哪个线程先执行(java程序就是抢占式调度)。
二、创建线程的方式
1.通过继承来创建线程 (extends Thread)
1.首先创建一个MyThread类去继承Thread方法,然后重写Thread中的run方法
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
2.创建一个测试类Test01
public class Test01 {
public static void main(String[] args) {
// 创建线程对象
MyThread t1 = new MyThread();
// 调用start方法, 启动线程
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"线程执行了:"+i);
}
}
}
我们会发现一个现象,我们在控制台中,每次运行的结果都不一样。这就是发生了两个线程互相抢占CPU的使用权导致的。
2.通过接口来创建线程(实现Runnable接口)
首先,我们创建一个MyRunnable类去实现Runnable接口,并重写run方法,设置线程任务。
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
}
}
}
创建测试类TestRunnable
public class TestRunnable {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
/**
* Thread(Runnable target)
*/
Thread t1 = new Thread(myRunnable);
//调用Thread中的start方法,开启线程
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
}
}
}
关于这两种实现多线程的方式的区别:
1.继承Thread:继承只支持单继承,有继承的局限性
2.实现Runnable:没有继承的局限性, MyThread extends (父类) implements Runnable
3.使用匿名内部类创建多线程
创建测试类noNameTest
public class NoNa么Thread{
public static void main(String[] args) {
/*
Thread(Runnable r)
Thread(Runnable target, String name) :name指的是给线程设置名字
*/
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
}
}
},"线程1").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i+"次");
}
}
},"线程2").start();
}
}
1.new Thread对应了Thread t1 = new Thread(myRunnable)的红色字体
2.(new Runnable(){
public void run(){
(....................方法体)
}
})对应了蓝色字体
4.多线程在内存中的运行原理
我们的程序从main方法开始运行,main方法在栈内存中开辟了空间,逐行向下执行。
首先在堆内存中创建了MyThread对象(包括成员变量,方法表等),在栈内存中有MyThread类型的t1指向堆中MyThread的地址。
(关于内存分析,详见:关于面向对象的内存分析-CSDN博客)
然后t1.start()创建线程对象,会创建一个新的栈空间(属于t1线程),在t1中找run方法并运行。
所以:开启一个线程就会开启一个新的栈空间去运行对应的线程代码。同一个线程对象不能连续调用多次start,如果想再次调用start,那就需要new一个新对象。
5.Thread类中的主要方法
void start() -> 开启线程,jvm自动调用run方法
void run() -> 设置线程任务,这个run方法是Thread重写的接口Runnable中的run方法
String getName() -> 获取线程名字
void setName(String name) -> 给线程设置名字
static Thread currentThread() -> 获取正在执行的线程对象(此方法在哪个线程中使用,获取的就是哪个线程对象)
static void sleep(long millis)->线程睡眠,超时后自动醒来继续执行,传递的是毫秒值
关于为什么在重写的run方法中有异常只能try,不能throws
原因:继承的Thread中的run方法没有抛异常,所以在子类中重写完run方法之后就不能抛,只能try...catch
6.Thread类中的其他方法
void setPriority(int newPriority)
-> 设置线程优先级,优先级越高的线程,抢到CPU使用权的几率越大,但是不稳定
int getPriority() -> 获取线程优先级
void setDaemon(boolean on)
-> 设置为守护线程,当非守护线程执行完毕,
守护线程就要结束,但是守护线程也不是立马结束,当非守护线程结束之后,
在告知的过程中,守护线程会执行,只不过执行到半路就结束了
static void yield() -> 礼让线程,让当前线程让出CPU使用权
void join() -> 插入线程或者叫做插队线程
6.1.1 线程优先级
依然沿用MyThread类
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
创建测试类Test02
public class Test02 {
public static void main(String[] args) {
//创建两个线程对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//更改两个线程对象的名字以便识别
t1.setName("线程1");
t2.setName("线程2");
//设置两个线程优先级
t1.setPriority(1);
t2.setPriority(10);
//启动两个线程
t1.start();
t2.start();
}
}
线程优先级默认为5,最大值为10,最小值为1
6.1.2 守护线程
依然沿用MyThread类
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
创建GuardianThread类作为守护线程
public class GuardianThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("守护线程运行了"+i+"次");
}
}
}
创建测试类Test03
public class Test03 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
GuardianThread guardianThread = new GuardianThread();
//将GuardianThread设置为守护线程
guardianThread.setDaemon(true);
//启动两个线程
myThread.start();
guardianThread.start();
}
}
运行结果如下:

明显,当非守护线程结束时,守护线程也会结束,只不过告知需要时间,结束会有延迟。
6.1.3 礼让线程
场景说明:如果两个线程一起执行,可能会执行一会儿线程A,再执行一会线程B,或者可能线程A执行完毕了,线程B再执行.
那么如果想让尽量让两个线程交替执行,就需要用到礼让线程
注意:只是尽可能的平衡,不是绝对的你来我往,有可能线程A线程执行,然后礼让了,但是回头A又抢到CPU使用权了。
创建yieldThread类
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
//调用yield方法
Thread.yield();
}
}
}
创建测试类Test04
public class Test04 {
public static void main(String[] args) {
yieldThread y1 = new yieldThread();
yieldThread y2 = new yieldThread();
y1.setName("y1线程");
y2.setName("y2线程");
y1.start();
y2.start();
}
}
6.1.4 插入线程
继续沿用MyThread类
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
创建测试类Test05
public class Test01 {
public static void main(String[] args) throws InterruptedException {
MyThread1 t1 = new MyThread1();
t1.setName("线程1");
t1.start();
/*
表示把t1插入到当前线程之前,t1要插到main线程之前,所以当前线程就是main线程
*/
t1.join();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"执行了......"+i);
}
}
}

2万+

被折叠的 条评论
为什么被折叠?



