线程的学习
创始人
2024-02-17 16:21:32
0

v# 1. 线程基本概念

1.1进程

  1. 进程是指运行中的程序,比如启动了QQ,就相当于启动了一个进程,操作系统会为该进程分配空间;当我们使用迅雷,就相当于又启动了一个进程,操作系统将为迅雷分配新的内存空间;
  2. 进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程,有它自身的产生、存在、消亡的过程;
  3. Run代表启动了一个进程,会马上进入main方法,这时进程就开启了一个线程,即主线程;
    在这里插入图片描述
    这时再启用start()方法就会在主线程里面再启动一个子线程:Thread-0;
    子线程在执行的时候,主线程不会阻塞,会继续执行;
    在这里插入图片描述

1.2线程

  1. 线程由进程创建的,是进程的一个实体;
  2. 一个进程可以拥有多个线程;
  3. 单线程:在同一个时刻,只允许执行一个线程;
  4. 多线程:同一个时刻,可以执行多个线程。比如,一个qq进程,可以同时打开多个聊天窗口;一个迅雷进程,可以同时下载多个文件;

1.3 并发

(1)单核CPU实现多任务;(2)同一个CPU,在某个时刻,多个任务交替执行;CPU速度很快,造成“QQ”和“迅雷”貌似同时执行的错觉;
在这里插入图片描述

1.4 并行

(1)在同一个时刻多个任务同时执行,多核CPU可以实现并行;

在这里插入图片描述

获取当前电脑Cpu数量
在这里插入图片描述

3. 线程基本使用

在这里插入图片描述

3.1 继承Thread类

  1. 当一个类继承了Thread类,该类就可以当作线程使用;
  2. Thread类实现了Runnable接口的run方法
    在这里插入图片描述
  3. 重写run方法,实现自己的业务逻辑
    在这里插入图片描述
  4. 调用start()方法会调用run()方法
    在这里插入图片描述

3.1.1 子线程

  1. Run代表启动了一个进程,会马上进入main方法,这时进程就开启了一个线程,即主线程;
  2. 这时再启用start()方法就会在主线程里面再启动一个子线程:Thread-0;
  3. 子线程在执行的时候,主线程不会阻塞;
  4. 子线程、主线程会交替执行;
    在这里插入图片描述
  5. 主线程的名称是main,子线程的名称从Thread-0开始;

3.1.1.1 JConsole监控

  • 验证:使用JConsole监控线程执行情况:
    在这里插入图片描述
    在这里插入图片描述

3.1.1.2 直接run()方法

  1. 此时的run()方法只是个普通方法,直接调用并不会开启子线程,此时Thread.currentThread().getName()是main
  2. 直接调用run方法不会开启子线程,默认在主线程内执行;
    在这里插入图片描述
  3. 会把run()方法执行完毕,才向下执行,串行化地执行,程序会阻塞;
    在这里插入图片描述

3.1.1.2 调用start()方法

  • 真正实现多线程的效果,是start0()方法
  1. cat调用start()方法,进入到Thread类的start()方法,start()方法调用start0()方法;
    在这里插入图片描述
  2. 进入到Thread类中的start()方法;start0()是本地方法,JVM调用,用C/C++编写;
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.2 实现Runable接口

  1. 假如一个A类继承了B类,那么它就不能再继承Thread类,这个时候让A类实现Runable接口,同样能实现线程;
  2. Runable接口里没有start()方法,只有个run方法;
    在这里插入图片描述
    解决方案:dog对象赋给了Thread类中的target,thread.start()方法调用start0()方法,start0()方法调用本类的run方法,再调用target的run方法;在这里插入图片描述

3.2.1 线程代理模式

用代码模拟实现Runnable接口实现线程的机制;

  1. 把Proxy类当作Thread类
public class Thread02 {public static void main(String[] args) {Tiger tiger = new Tiger();//实现了Runnable接口ThreadProxy threadProxy = new ThreadProxy(tiger);threadProxy.start();}
}

实现步骤: 先创建一个Proxy类,继承Runnable

class ThreadProxy implements Runnable {//把Proxy类当作 ThreadProxy 线程代理private Runnable target = null;@Overridepublic void run() {if (target != null) {target.run();//动态绑定(运行类型Tiger)}}public ThreadProxy(Runnable target) {this.target = target;}public void start() {start0();//真正实现多线程的方法}public void start0() {run();}
}
  1. 再创建一个普通类,实现Runnable接口;
class Tiger extends Animal implements Runnable{@Overridepublic void run() {System.out.println("老虎发威~~");}
}

3.2.2 多线程机制

两个子线程会交替执行
在这里插入图片描述
在这里,主线程看不到
在这里插入图片描述
在这里插入图片描述

3.3 继承Thread Vs 实现Runnable接口

  1. 本质没有区别,因为Thread类本身就实现了Runnable接口的run()方法
  2. 但是实现Runnable接口的方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制;(建议使用Runnable接口)
  3. 继承Thread实现线程无法做到资源共享,并且受到单继承的限制;
    在这里插入图片描述

3.3.1 售票系统超卖问题

sleep()要在–tickets前面才能超卖

public class SellTicket {public static void main(String[] args) {/*SellTicket1 sellTicket1 = new SellTicket1();SellTicket1 sellTicket2 = new SellTicket1();SellTicket1 sellTicket3 = new SellTicket1();sellTicket1.start();sellTicket2.start();sellTicket3.start();*/SellTicket1 sellTicket1 = new SellTicket1();Thread thread1 = new Thread(sellTicket1);Thread thread2 = new Thread(sellTicket1);Thread thread3 = new Thread(sellTicket1);thread1.start();thread2.start();thread3.start();}
}class SellTicket1 extends Thread {//private static int tickets = 100;private int tickets = 100;int count = 0;@Overridepublic void run() {while (true) {if (tickets <= 0) {break;}System.out.println("窗台:" + Thread.currentThread().getName() +"出售了" + (++count) + "张票,剩余票数:" + (--tickets));try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}
}

3.3.2 线程终止

  1. 当线程完成任务后,会自动退出;
  2. 可以通过变量来控制run方法退出线程,即通知方式

3.3.2.1 通知线程退出

启动一个线程t,要求在main线程中去停止线程t
解决方案:在主线程中通过Setter方法将loop变量设置为false
在这里插入图片描述
在这里插入图片描述

3.3.3 线程基本方法

方法名作用
setName设置线程名称
getName获取线程名称
setPriority设置线程优先级
getPriority获取线程优先级
interrupt中断线程

在这里插入图片描述

3.3.3 线程中断

  1. interrupt中断线程,但并没有真正的结束线程,所以一般用于中断正在休眠的线程;(中断休眠,唤醒线程)
  2. sleep(),线程的静态方法,使当前线程休眠;
public class ThreadMethod_ {public static void main(String[] args) throws InterruptedException {T t = new T();t.setName("赵志伟");//设置线程名称t.setPriority(Thread.MIN_PRIORITY);//设置线程优先级t.start();//启动了子线程System.out.println(t.getName());//获取线程名称//主线程打印5个hi,然后就中断子线程的休眠for (int i = 0; i < 5; i++) {Thread.sleep(1000);System.out.println("hi~~" + i);}System.out.println(t.getName() + "线程的优先级" + t.getPriority());t.interrupt();//执行到interrupt()方法时,中断线程的休眠}
}class T extends Thread {@Overridepublic void run() {while (true) {for (int i = 0; i < 100; i++) {//获取当前线程的名称System.out.println(Thread.currentThread().getName() + " 吃包子~~" + i);}try {System.out.println(Thread.currentThread().getName() + " 正在休眠中~~");Thread.sleep(20 * 1000);} catch (InterruptedException e) {//当该线程执行到一个interrupt 方法时,就会catch 一个异常,可以加入自己的业务代码// InterruptedException捕获了一个中断异常System.out.println(Thread.currentThread().getName() + "被中断了");}}}
}

3.3.4 线程礼让

yield:线程的礼让,让出cpu,让其它线程执行,但礼让的时间不确定,所以不一定礼让成功;

3.3.5 线程插队

join:线程插队,一旦插入成功,则先执行完插入的线程的所有的任务;

public class Thread03 {public static void main(String[] args) throws InterruptedException {T t = new T();Thread thread = new Thread(t);thread.start();for (int i = 1; i <= 20; i++) {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + "(小弟)吃了" + i + "个");if (i == 10) {System.out.println("main线程(小弟)让子线程(大哥)先吃");
//                thread.join();//子线程(大哥)插队,大哥吃完小弟再吃Thread.yield();//main线程(小弟)礼让,不一定成功...}}}
}class T implements Runnable {private int count = 0;private boolean loop = true;@Overridepublic void run() {while (loop) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("子线程(老大)吃了" + (++count) + "个");if (count == 20) {//吃到20个结束System.out.println("子线程(大哥)吃完了,main线程(小弟)继续吃");break;}}}public void setLoop(boolean loop) {this.loop = loop;}
}

3.3.5.1 线程插队练习

public class ThreadMethodExercise {public static void main(String[] args) throws InterruptedException {T1 t1 = new T1();Thread thread = new Thread(t1);for (int i = 1; i <= 10; i++) {Thread.sleep(1000);System.out.println("main线程:hi " + i);if (i == 5) {thread.start();thread.join();//子线程启动后直接插队}}}
}class T1 implements Runnable {private int count;@Overridepublic void run() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("子线程: hello " + (++count));if (count == 10) {break;}}}
}

3.3.6 守护线程

用户线程:也叫工作线程、当线程的任务执行完或通知方式结束;
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束;
常见的守护线程:垃圾回收机制;

主线程结束,子线程并不会结束;如果我们希望主线程结束时,子线程也结束,可以将子线程设置为守护线程;

public class ThreadMehod03 {public static void main(String[] args) {MyDaemonThread myDaemonThread = new MyDaemonThread();Thread thread = new Thread(myDaemonThread);thread.setDaemon(true);//将子线程设置为守护线程thread.start();for (int i = 1; i <= 10; i++) {System.out.println("主线程" + Thread.currentThread().getName() + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
}class MyDaemonThread implements Runnable {@Overridepublic void run() {for (; ; ) {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("子线程" + Thread.currentThread().getName());}}
}

3.3.7 线程的声明周期

  1. JDK中用Thread.State枚举表示了线程的七种(六种)状态
    在这里插入图片描述
public class ThreadState_ {public static void main(String[] args) throws InterruptedException {T2 t2 = new T2();Thread thread = new Thread(t2);System.out.println(thread.getName() + "状态 " + thread.getState());//NEWthread.start();//子线程和主线程同时执行/*for (int i = 0; i < 10; i++) {Thread.sleep(1000);//主线程休眠、主线程输出System.out.println(thread.getName() + "状态 " + thread.getState());//RUNNABLE}*/while (Thread.State.TERMINATED != thread.getState()) {Thread.sleep(1000);System.out.println(thread.getName() + "状态 " + thread.getState());//RUNNABLE、TIMED_WAITING}System.out.println(thread.getName() + "状态 " + thread.getState());//TERMINATED}
}class T2 implements Runnable {@Overridepublic void run() {while (true) {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + " " + i);//子线程输出try {Thread.sleep(1000);//子线程休眠} catch (InterruptedException e) {e.printStackTrace();}}break;}}
}

3.3.8 线程同步机制

  1. 在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任意一个时刻,最多有一个线程访问,以保证数据的完整性;
  2. 也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其它线程都不可以对这个内存地址进行操作,知道该线程完成操作后,其它线程才能对该内存地址进行操作;

3.3.9 互斥锁

  1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性;

  2. 每个对象都对应一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象;

  3. 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问;

  4. 同步的局限性:会导致程序的执行效率降低;

  5. 同步方法(非静态的)的锁可以是this,也可以是其它对象(要求是同一个对象)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  6. 同步方法(静态的)的锁为当前类本身;

  7. 方法上加锁、代码块上加锁;

  8. 同步方法如果没有使用static修饰,默认锁对象为this;

  9. 如果方法使用static修饰,默认锁对象:当前类.class; 如果想在静态方法中,实现一个同步代码块,写类本身;
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.4.0 死锁

public class DeadLock_ {public static void main(String[] args) {DeadLockDemo deadLockDemo = new DeadLockDemo(true);deadLockDemo.setName("A线程");deadLockDemo.start();DeadLockDemo deadLockDemo1 = new DeadLockDemo(false);deadLockDemo1.setName("B线程");deadLockDemo1.start();}
}class DeadLockDemo extends Thread {static Object o1 = new Object();//保证多线程,共享一个对象,使用staticstatic Object o2 = new Object();boolean flag;public DeadLockDemo(boolean flag) {//构造器this.flag = flag;}@Overridepublic void run() {//(1)如果flag为true,线程A 就会先得到/持有 o1对象锁,然后尝试去获取o2对象锁//(2)如果flag为true,线程A 就会先得到/持有 o1对象锁,然后尝试去获取o2对象锁if (flag) {synchronized (o1) {System.out.println(Thread.currentThread().getName() + "进入1");synchronized (o2) {System.out.println(Thread.currentThread().getName() + "进入2");}}} else {synchronized (o2) {System.out.println(Thread.currentThread().getName() + "进入3");synchronized (o1) {System.out.println(Thread.currentThread().getName() + "进入4");}}}}
}

3.4.1 释放锁

wait():当前线程暂停,并释放锁;
sleep()、yield():不会释放锁;

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...