导图社区 第八章 多线程
多线程,线程:每个运行的程序都是一个进程,在一个进程中还可以看作程序执行的一条条线索,称为线程。操作系统中的每一个进程中都至少存在一个线程。
编辑于2023-06-14 11:54:12 重庆第八章 多线程
线程概述
计算机能够同时完成的多项任务叫做多线程技术
进程
在操作系统中,每个独立执行的程序都可称为一个进程
线程
每个运行的程序都是一个进程,在一个进程中还可以看作程序执行的一条条线索,称为线程。操作系统中的每一个进程中都至少存在一个线程。
缺点
创建进程比创建线程开销大
进程间通信比线程间通信慢
线程的创建
继承Thread类创建多线程
public class Example02 { public static void main(String[] args) { MyThread myThread=new MyThread(); myThread.start(); while(true){ System.out.println("main()方法在运行"); } }}class MyThread extends Thread{ public void run(){ while(true){ System.out.println("MyThread类的run()方法在运行"); } }}
实现Runnable接口创建多线程
public class Example03 { public static void main(String[] args) { MyThread myThread=new MyThread(); Thread thread= new Thread(myThread); thread.start(); while(true){ System.out.println("main()方法在运行"); } }}class MyThread implements Runnable{ public void run(){ while(true){ System.out.println("MyThread类的run()方法在运行"); } }}
两种实现多线程方式的对比分析
通过继承Thread类的方式创建多线程
public class Example04 { public static void main(String[] args) { new TicketWindow().start(); new TicketWindow().start(); new TicketWindow().start(); new TicketWindow().start(); }}class TicketWindow extends Thread{ private int tickets=100; public void run(){ while(true){ if(tickets>0){ Thread th=Thread.currentThread(); String th_name = th.getName(); System.out.println(th_name+"正在发售第"+tickets--+"张票"); } } }}
通过实现Runnable接口的方式创建多线程
public class Example05 { public static void main(String[] args) { TicketWindow tw =new TicketWindow(); new Thread(tw,"窗口1").start(); new Thread(tw,"窗口2").start(); new Thread(tw,"窗口3").start(); new Thread(tw,"窗口4").start(); }}class TicketWindow implements Runnable{ private int tickets=100; public void run(){ while(true){ if(tickets>0){ Thread th=Thread.currentThread(); String th_name = th.getName(); System.out.println(th_name+"正在发售第"+tickets--+"张票"); } } }}
线程的生命周期及状态转换
新建状态(New)
创建一个线程对象后,该线程对象就处于新建状态,此时它不能运行,只是java虚拟机为其分配了内存
就绪状态(Runnable)
当线程对象调用了start()方法后,该线程就进入就绪状态。位于线程队列中,只具备了运行的条件,能否获得CPU的使用权并开始运行,还需要等待系统调度。
运行状态(Running)
当线程获得了CPU的使用权,并开始执行,则该线程处于运行状态。但不会一直处于运行状态,使用完系统分配的时间后,系统将收回该线程占用的CPU资源,只有处于就绪状态的线程才能转换到运行状态。
阻塞状态(Blocked)
当某个线程占用着CPU的资源长时间不适用时,会进入阻塞状态,不能进入排队队列,消除阻塞的原因后,才可以转入就绪状态。
死亡状态(Terminated)
线程正常执行完毕,就会进入死亡状态,该线程就不再拥有运行的资格,且不会再转换到其他状态。
线程的调度
线程的优先级
如果要对线程进行调度,就设置线程的优先级。优先级越高的线程获得CPU执行的机会越大,线程的优先级用1~10表示,数字越大优先级越高。
Thread类的优先级常量
Thread类的静态常量: static int MAX_PRIORLTY 功能:表示线程的最高优先级,值为10. static int MIN_PRIORLTY 功能:表示线程的最低优先级,值为1. static int NORM_PRIORLTY 功能:表示线程的普通优先级,值为5.
线程休眠
如果希望人为地控制线程,使正在执行的线程暂停,就可以使用静态方法sleep(long millis),进入休眠等待状态。当前线程调用sleep(long millis)方法后,在指定时间(单位毫秒)内该线程不会执行。
sleep()是静态方法,只是控制当前正在运行的线程休眠,而不能控制其他线程休眠。当线程休眠时间结束后,线程就会返回到就绪状态,而不是立即开始运行。
线程让步
是指正在执行的线程,在某些情况下将CPU资源让其他线程执行
线程让步可也通过yield()方法来实现,该方法和sleep()方法有点相似,都可以让当前正在运行的线程暂停,区别在于yield()方法不会阻塞该线程,它只是将线程转换成继续状态。只有与当前线程优先级相同或者更高的线程才能获得执行的机会。
线程插队
提供了一个join()方法来实现这个“功能”。当某个线程中调用其他线程的join()方法时。调用的线程将被阻塞,直到被join()方法加入的线程执行完成后它才会继续运行。
多线程同步
线程安全问题
限制某个资源在同一时刻只能被一个线程访问
同步代码块
当多个线程使用同一个共享资源时,可以将处理共享资源的代码块放在一个使用synchronized关键字修饰的代码块中,这个代码块称为同步代码块。
synchronized(lock){操作共享资源代码块}同代码块中的锁对象可以是任意类型的对象。但多个线程的共享的锁对象必须是唯一的。“任意”说的是共享锁对象的类型。锁对象的创建代码不能放到run()方法中,否则每个线程运行到run()方法都会创建一个新对象,这样每个线程都会有一个不同的锁,每个锁都有自己的标志位,这样的线程之间便不能产生同步的效果。
同步方法
当把共享资源的操作放在synchronized定义的区域内时,便为这些操作加了同步锁。在方法前面同样可以使用synchronized关键字来修饰,被修饰的方法为同步方法,它能实现与同步代码块相同的功能。
synchronized 返回值类型 方法后([参数1,...]){}
死锁问题
两个线程在运行时都在等待对方的锁,这样便造成了程序的停滞,这种现象称为死锁。两个线程都需要对方所占用的锁,但是都无法释放自己所拥有的锁,于是这两个线程都处于挂起状态,则造成了死锁。