黑马程序员——java多线程

本文详细解析了程序、进程与多任务的概念,深入探讨了线程的定义及其实现方式,阐述了线程的生命周期及其状态变化,包括运行、阻塞和死亡状态。同时,文章介绍了多线程的同步机制,包括synchronized关键字的应用以及如何避免死锁,最后讨论了等待通知机制在多线程通信中的应用。
一、程序、进程与多任务
程序(program)是对数据描述与操作的代码的集合,是应用程序执行的脚本。
进程(process)是程序的一次执行过程,是系统运行程序的基本单位。程序是静态的,进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的过程。
多任务(multi task)在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程。

二、线程
线程(thread):比进程更小的运行单位,是程序中单个顺序的流控制。一个进程中可以包含多个线程。
简单来讲,线程是一个独立的执行流,是进程内部的一个独立执行单元,相当于一个子程序。
一个进程中的所有线程都在该进程的虚拟地址空间中,使用该进程的全局变量和系统资源。
操作系统给每个线程分配不同的CPU时间片,在某一时刻,CPU只执行一个时间片内的线程,多个时间片中的相应线程在CPU内轮流执行。

三、创建多线程

1.创建 java.lang.Thread 类的子类,重写该类的 run方 法
2.创建 java.lang.Runnable接 口的实现类,实现接口中的 run 方法
public class ThreadTest {

	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运行");
		}
	}
}

public class ThreadTest2 {

	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("run()运行");
		}
	}
}

四、线程的生命周期

运行状态(Running)
处在“可执行状态”的线程对象一旦获得了 CPU 控制权,就会转换到“执行状态”
在“执行状态”下,线程状态占用 CPU 时间片段,执行run 方法中的代码
处在“执行状态”下的线程可以调用 yield 方法,该方法用于主动出让 CPU 控制权。线程对象出让控制权后回到“可执行状态”,重新等待调度。
public class ThreadTest3 {

	public static void main(String[] args) {
		MyThread2 myThread1 = new MyThread2("t1");
		MyThread2 myThread2 = new MyThread2("t2");
		myThread1.start();
		myThread2.start();

	}
	

}

class MyThread2 extends Thread{
	MyThread2(String s){
		super(s);
	}
	public void run(){
		for(int i = 0; i < 1000; i++){
			System.out.println(getName() + ":" + i);
			if(i % 10 == 0)
				yield();
		}
	}
	
}


阻塞状态(Blocking)
线程在“执行状态”下由于受某种条件的影响会被迫出让CPU控制权,进入“阻塞状态”。
进入阻塞状态的三种情况
调用sleep方法
调用join方法
执行I/O操作
阻塞状态(Blocking)
线程在“执行状态”下由于受某种条件的影响会被迫出让CPU控制权,进入“阻塞状态”。
进入阻塞状态的三种情况
调用sleep方法
调用join方法
执行I/O操作
sleep()方法
调用sleep方法
public void sleep(long millis) 
Thread类的sleep方法用于让当前线程暂时休眠一段时间
参数 millis 的单位是毫秒
public class SleepThreadTest extends Thread{
	public void run(){
		while(true){
			System.out.println("每隔一秒钟输出一次");
			try{
				sleep(1000);
			}catch(InterruptedExcption e){
				e.printStackTrace();
			}
		}
	}
}


调用 join 方法(合并某个线程)
处在“执行状态”的线程如果调用了其他线程的 join 方法,将被挂起进入“阻塞状态”
目标线程执行完毕后才会解除阻塞,回到 “可执行状态”
执行I/O操作
线程在执行过程中如果因为访问外部资源(等待用户键盘输入、访问网络)时发生了阻塞,也会导致当前线程进入“阻塞状态”。
解除阻塞
睡眠状态超时
调用 join 后等待其他线程执行完毕
I/O 操作执行完毕
调用阻塞线程的 interrupt 方法(线程睡眠时,调用该线程的interrupt方法会抛出InterruptedException
死亡状态(Dead):处于“执行状态”的线程一旦从run方法返回(无论是正常退出还是抛出异常),就会进入“死亡状态”。
已经“死亡”的线程不能重新运行,否则会抛出IllegalThreadStateException
可以使用 Thread 类的 isAlive方法判断线程是否活着

多线程的同步:    

                  多线程并发访问同一个对象(临界资源),如果不对线程进行同步控制,破坏了原子操作(不可再分的操作),则会造成临界资源(两个线程同时访问的资源)的数据不一致。  

 

                  每一个对象都有一个互斥的锁标记和一个锁池。当线程拥有这个对象的锁标记时才能访问这个资源,没有锁标记便进入锁池,保证在同步代码块中只有一个线程,解决了多线程同步控制的问题。

                 

                  关键字:synchronized   //线程在同步代码中必须采用串行访问

                           synchronized修饰代码块:对括号内的对象object加锁,只有拿到对象锁标记的线程才能进入该代码块。

                              

  publicvoid push(char c){
                                            synchronized(object){    //object只要是对象就可以,但必须保证是同一对象
                                                     ……
                                                     同步代码
                                                     ……
                                            }
                                }

                               

                           synchronized修饰方法:在整个方法范围内对当前对象的加锁,只有拿到对象锁标记的线程才能执行该方法。尽可能的少用

                                

publicsynchronized void push(char c) {
                                             ……
                                             同步代码
                                             ……  
                                    }

                                     

                  一个线程可以同时拥有多个对象的锁标记,锁标记如果过多,就会出现线程等待其他线程释放锁标记,而又都不释放自己的锁标记供其他线程运行的状况,造成死锁。                                   

 

                  静态方法可以是同步方法:但是它所锁的并不是当前对象,是类对象。

                  抽象方法不能是synchronized同步的方法。

                  构造方法不能是synchronized同步的方法。

                          

                  线程因为未拿到锁标记而发生阻塞进入锁池(lock pool)。每个对象都有自己的一个锁池的空间,用于放置等待运行的线程。由系统决定哪个线程拿到锁标记并运行

                 

                  利用Collections类中的synchronizedXxxx(Xxxx ss)方法可以得到相应集合的线程安全的集合

                 

                  注意:

                           在同步语句块中不能直接操作对象锁正在使用的对象。

                           对象与锁一一对应。

                           同步依赖对象锁,锁对象相同,同步语句串行,锁对象不同,同步语句并行。

                           顺序锁,不要回调,反向打开。

                           能不用同步就不用同步,有数据共享冲突时才使用同步。

                 

         等待通知机制:

                  线程间通信使用的空间称之为对象的等待对列(wait pool),该队列也是属于对象的空间的。

                 

                  使用Object类中wait()的方法,在运行状态中,线程调用wait(),此时表示线程将释放自己所有的锁标记和CPU的占用,同时进入这个对象的等待池。等待池的状态也是阻塞状态,只不过线程释放自己的锁标记。只有在对该对象加锁的同步代码块里,才能掉用该对象的wait(),表示线程将会释放所有锁标记,进入等待队列,线程将进入等待队列状态。

                 

                  一个线程进入了一个对对象加锁的同步代码块,并对该对象调用了wait()方法,释放自己拥有的所有锁标记,进入该对象等待队列,另一个线程获得了该对象的锁标记,进入代码块对该对象调用了notify()方法,就会从等待队列里释放出一线程,释放出的这个线程要继续运行就还要进入那个同步代码块,因为得不到要访问代码块对象的锁标记,而进入该对象的锁池,等待锁标记释放。

                 

                  什么情况下释放锁:

                           同类代码执行完毕。

                           异常未处理,错误退出。

                           调用wait()。

                 

                  相关方法:

                           1) wait():交出锁和CPU的占用;

                           2)notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。

                           3)notifyAll(): 将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行

                          

                  注意:

                           用notifyAll()取代notify(),因为在调用notify()方法时,是由系统决定释放出哪个线程。

                           只能对加锁的资源进行wait()和notify()。

                           判断是否进行等待wait()时,用while代替if来进行判断。







AI-PPT 一键生成 PPT:用户输入主题关键词,AI-PPT 可快速生成完整 PPT,涵盖标题、正文、段落结构等,还支持对话式生成,用户可在 AI 交互窗口边查看边修改。 文档导入转 PPT:支持导入 Word、Excel、PDF 等多种格式文档,自动解析文档结构,将其转换为结构清晰、排版规范的 PPT,有保持原文和智能优化两种模式。 AI-PPT 对话 实时问答:用户上传 PPT 或 PPTX 文件后,可针对演示内容进行提问,AI 实时提供解答,帮助用户快速理解内容。 多角度内容分析:对 PPT 内容进行多角度分析,提供全面视野,帮助用户更好地把握内容结构和重点。 多语言对话支持:支持多语言对话,打破语言障碍,方便不同语言背景的用户使用。 AI - 绘图 文生图:用户输入文字描述,即可生成符合语义的不同风格图像,如油画、水彩、中国画等,支持中英文双语输入。 图生图:用户上传图片并输入描述,AI - 绘图能够根据参考图和描述生成新的风格化图像,适用于需要特定风格或元素的创作需求。 图像编辑:提供如 AI 超清、AI 扩图、AI 无痕消除等功能,用户可以上传图片进行细节修改和优化,提升图片质量。 AI - 文稿 文案生成:能够根据用户需求生成多种类型的文章,如市场营销文案、技术文档、内部沟通内容等,提升文案质量和创作效率。 文章润色:对已有文章进行改善和优化,包括语言表达、逻辑连贯性、内容流畅度等方面,使文章更符合用户期望和风格。 文章续写:AI 技术理解文本语境,为用户提供新的想法、补充资料或更深层次的见解,帮助用户丰富文档内容。 AI - 医生 智能健康咨询:包括症状自查,用户输入不适症状,AI 结合病史等信息提供疾病可能性分析与初步建议;用药指导,支持查询药品适应症、禁忌症等,并预警潜在冲突;中医辨证,提供体质辨识与调理建议。 医学报告解读:用户上传体检报告
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值