线程思路

new 新建
start 就绪
1:就绪 2:运行
wait 等待

sleep 睡眠

join 结束

线程乐乐总结
12.28 1.线程总结

第一步理解线程和进程的区别:
(1)进程是什么:进程可以比作为你开启的某个服务,比如IE浏览器、qq等这些从狭义上来说都是进程,一个进程中可以存在多个线程。
(2)线程是什么:进程中的一个执行流程。
(3)区别为什么:1.每个进程都需要操作系统为其分配独立的内存空间,而同一个进程中的多个线程共享这块空间,共享内存等资源。
2.每次调用java.exe的时候,操作系统都会启动一个Java虚拟机进程,当启动Java虚拟机进程时候,Java虚拟机都会创建一个主线程,该线程会从程序入口main方法开始执行。 3.Java虚拟机每启动一个线程,就给会给该线程分配一个线程方法栈,用来存放相关信息(比如局部变量等),线程就在这个栈上运行。所以Java对象中的局部变量都是线程安全的,但实例变量及类变量由于不是保存在栈中,所以不是线程安全的。
第二步创建线程和启动:
如何创建线程
继承Thread类创建线程类,重写run()方法,创建Thread子类的实例,创建线程对象。调用start()方法启动线程
实现Runnable接口创建线程类,重写run()方法,创建Runnable实现类的实例作为Thread的target对象,即该Thread对象才是真正的线程对象。
第三步线程的生命周期:
1.新建状态
指new出来一个线程或者Thread类或者其子类建立一个线程对象后,该线程就处于一个新生状态。一个线程处于新生状态时都有自己的内存空间,调用start方法后进入就绪状态。
2.就绪状态
处于就绪状态的线程具备了运行条件,但还没有分配cpu,处于线程的就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。因为cpu的调度不一定是按照先进先出的顺序来调度的),等待系统为其分配cpu,等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态到执行状态,系统挑选的动作为“cpu调度”。线程一旦获取cpu,线程就处于运行状态并调用自己的run方法。
提示:如果希望子线程调用start方法后立即执行,可以使用Thread.sleep()方式使主线程睡眠一会,转去执行子线程。
3.运行状态
处于运行状态的线程最为复杂,它可以变为阻塞状态,就绪状态和死亡状态。
处于就绪状态的线程,如果获取了cpu的调度,就会从就绪状态转变为运行状态,执行run()方法中任务,如果该线程失去了cpu调度,就又会从运行状态转为就绪状态。也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。
什么情况下,线程会由运行状态到阻塞状态:
(1)线程调用sleep方法主动放弃所占用的系统资源
(2)线程调用一个阻塞式IO方法,在方法返回之前,线程被阻塞。
(3)线程试图获得一个同步监视器,但更改同步监视器正在被其他线程所持有
(4)线程在等待某个通知
(5)程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应尽量避免使用该方法
当线程run()方法执行完,或者被强制性的终止,例如出现异常,调用了stop(),desyory()方法等,就会从运行状态转变为死亡状态。
4.阻塞状态
处于运行状态的线程在某些情况下,如执行sleep(睡眠)方法,或等待I/O设备等资源,将CPU并暂停自己的状态。进入阻塞状态。
在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因消除时,如睡眠时间已到或者I/O设备空闲下来,线程变回转入就绪状态,重新就绪队列中排队等待,当系统选中后从原来停止的位置开始继续运行,有三种方法可以暂停Thread执行.
5.死亡状态
当线程的run()方法执行完,或者又被强制性的终止就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死去的线程调用start()方法,会抛出java.lang.IllegalThreadStateException异常
第四步线程管理
Java提供了一些便捷的方法用于会线程状态的控制。具体如下:
1.线程睡眠——————sleep
如何我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态则可以通过调用Thread的sleep方法。
Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。但是不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。因为使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。

	2.线程让步————yield
	yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态的方法,它也可以让当前正在执行的线程暂停,让出cpu资源给其他的线程。但是和sleep()方法不同是,它不会进入到阻塞状态,而是进入到就绪状态。yield()方法只是让当前线程暂停一下,重新进入就绪的线程池中,让系统的线程调度器重新调度器重新调度一次,完全可能出现的情况:当某个线程调用yield()方法之后,线程调度器又将其调度出来重新进入到运行状态执行。
	实际上,当某个线程调用了yield()方法暂停之后,优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程更有可能获得执行的机会,当然,只是有可能,因为我们不可能精确的干涉cpu调度线程
	
	注:关于sleep()方法和yield()方的区别如下:

	①、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态,所以有可能刚进入就绪状态,又被调度到运行状态。

	②、sleep方法声明抛出了InterruptedException,所以调用sleep方法的时候要捕获该异常,或者显示声明抛出该异常。而yield方法则没有声明抛出任务异常。

	③、sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法来控制并发线程的执行。
	
	3.线程的优先级问题————Thread类提供了
	setPriority(int newPriority)和getPriority()方法来设置和返回一个指定线程的优先级,其中setPriority方法的参数是一个整数,范围是1~·0之间,也可以使用Thread类提供的三个静态常量:
	MAX_PRIORITY   =10
	MIN_PRIORITY   =1
	NORM_PRIORITY   =5
	
	4.后台守护进程
	调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。守护线程的用途为:
	(1)守护线程通常用于执行的一些后台作业,例如在应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能
	(2)java垃圾回收也是守护线程。
	public final void setDaemon(boolean on)        将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。    
     该方法必须在启动线程前调用。 该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。   
	参数:
		on - 如果为 true,则将该线程标记为守护线程。    
	抛出:    
		IllegalThreadStateException - 如果该线程处于活动状态。    
		SecurityException - 如果当前线程无法修改该线程。
	5.正确结束线程
		Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!想要安全有效的结束一个线程,可以使用下面的方法:

	• 正常执行完run方法,然后结束掉;

	• 控制循环条件和判断条件的标识符来结束掉线程。

第五步线程同步:
关于锁与同步的要点:
1)、只能同步方法,而不能同步变量和类;
2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6)、线程睡眠时,它所持的任何锁都不会释放。
7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。
什么是线程同步:java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据的不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

	1.同步方法
		有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法,在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
		
		public synchronzied void save(){}
		
		注:synchronzied关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
	2.同步代码块
		线程中同步代码块,说明该代码块被一个或多个线程执行时必须执行完才能让下一个下一个线程执行。
		
		同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
	4.final关键字:只能为它修饰的成员变量赋一次值。
	5.synchronized方法与synchronized代码块的区别:
		synchronized代码块比synchronized方法的效率更高效    synchronzied代码块作用域为当前对象     synchronzied方法作用域当前实例
	6.synchronized 作用在普通方法与静态方法的区别:
		synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: 
		1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 
		2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 
		3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象; 
		4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

第六步线程通信:
1.借助Object类的wait()、notify()和notifyAll()实现通信
线程执行wait()后,就放弃了运行资格,处于冻结状态;
线程运行时,内存中会建立一个线程池,冻结状态的线程都存在于线程池中,notify()执行时唤醒的也是线程池中的线程,线程池中有多个线程时唤醒第一个被冻结的线程。
notifyAll(),唤醒线程池中的所有线程。
注:
(1)wait(),notify(),notifyall()都用在同步里面,因为这3个函数是对持有锁的线程进行操作,而只有同步才有锁,所以要使用在同步中;
(2)wait(),notify(),notifyall(),在使用时必须标识它们所操作的线程持有的锁,因为等待和唤醒必须是同一锁下的线程;而锁可以是任意对象,所以这3个方法都是Object类中的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值