




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Java中的多线程为什么会有多线程?多任务分时操作系统的出现(windows,linux)什么是分时操作系统可以同一时间执行多个程序的操作系统,用户边上网,边听歌操作系统为什么能同时执行多个任务?CPU同时执行多个程序?CPU只是将时间切割为时间片,然后将时间片分配给这些程序,获得时间片的程序开始执行,不等执行完毕,下个程序又获得时间片开始执行,这样多个程序轮流执行一段时间,由于现在cpu的高速计算能力,给人的感觉就像是多个程序在同时执行一样。多线程的概念进程的概念一个进程就是一个执行中的程序,每一个进程都有自己独立的一块内存空间,一组系统资源进程的特点每一个进程的内部数据和状态都是完全独立的
2、.因此可以想像创建并执行一个进程的系统开销是比较大的 多任务多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程 多线程的概念线程的概念线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制,在java中,程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程什么是多线程多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务.多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行 多线程的概念进程与线程的区别进程都有自己独立的一块内存空间,一组系统资源。线程是共享一块内存空间和一组系统资源,系统在产生一个线程,或者
3、在各个线程之间切换时,负担要比进程小的多 。线程也被称为轻负荷进程(light-weight process)一个进程中可以包含多个线程,线程总是属于某个进程,进程中的多个线程共享进程的内存。 多线程的概念一个线程实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上一个Java应用总是从main()方法开始运行,main()方法运行在一个线程内,它被称为主线程。Java中创建线程继承java.lang.Thread类实现java.lang.Runnable接口Java中的线程继承Java.lang.Thread继承Thread覆盖Thread的run()方法,将你要让线
4、程做的事写在run方法中。有时候可以需要用循环,使你的代码一直执行下去New 这个Thead子类,得到一个对象调用这个对象的start()方法。线程被启动,开始执行run()中的代码。Java中的线程例子这个例子实现一个定时线程,即线程在给定期间每隔一定时间(1秒),屏幕显示时间累计数(秒数),时间结束时线程自动停止、撤消。Java中的线程演示示例:继承Thread实现java.lang.Runnable接口由于继承了Thread后,类再不能继承别的类,所以一般我们采用实现Runnable接口的方法来创建线程提供一个实现接口Runnable的类,实现run()方法。得到这个类的实例A。New
5、一个Thread对象,同时A作为参数传入。例如:Thread runner=new Thread(A);调用start启动线程。例如:runner.start();Java中的线程例子这个例子与刚才哪个例子不同点在于要实现Runable接口,必须实现run()方法,否则将不能通过编译。而继承Thread类的线程机制则不一定要重载其run( )方法,线程类将自动调用其基类的run( )方法Java中的线程演示示例:实现Runnable接口线程的机制图 Java中的状态线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五种状态.一个具有生命的线程
6、,总是处于这五种状态之一:创建状态:线程对象已经创建,还没有在其上调用start()方法。(new thread)可运行状态:使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于可运行状态 。(Runnable)运行中状态:JVM调度选中一个Runnable的线程,使其占有CPU并转为运行中状态(Running).此时,系统真正执行线程的run()方法.Java中的状态等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回
7、到可运行状态。死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。 Java中的状态Java中的多线程java中多线程就是一个类或一个程序执行或管理多个线程执行任务的能力,每个线程可以独立于其他线程而独立运行,当然也可以和其他线程协同运行,一个类控制着它的所有线程,可以决定哪个线程得到优先级,哪个线程可以访问其他类的资源,哪个线程开始执行,哪个保持休眠状态。Java中的多线程线程
8、的执行机制同一时刻如果有多个线程处于可运行状态,则他们需要排队等待CPU资源.此时每个线程自动获得一个线程的优先级(priority),优先级的高低反映线程的重要或紧急程度.可运行状态的线程按优先级排队,线程调度依据优先级基础上的“先到先服务”原则 。线程调度管理器负责线程排队和CPU在线程序的分配,并由线程调度算法进行调度.当线程调度管理器选种某个线程时,该线程获得CPU资源而进入运行状态.线程调度是先占式调度,即如果在当前线程执行过程中一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行.先占式调度分为:独占式和分时方式.Java中的线程机制独占式独占方式下,当前执行线程将一直执行
9、下去,直到执行完或由于某种原因主动放弃CPU,或CPU被一个更高优先级的线程抢占分时方式 分时方式下,当前运行线程获得一个时间片,时间到时,即使没有执行完也要让出CPU,进入可运行状态,等待下一个时间片的调度.系统选中其他可运行状态的线程执行分时方式的系统使每个线程工作若干步,实现多线程同时运行Java中的线程机制线程的执行机制那么会打印出:My Name is Thread-0My Name is Thread-1My Name is Thread-2看了上面的结果,你可能会认为线程的执行顺序是依次执行的,但是那只是一般情况,千万不要以为这是线程的执行机制;影响线程执行顺序的因素有几点Jav
10、a中的线程执行机制演示示例:线程的执行机制首先看看前面提到的优先级别再看看结果:My Name is Thread-1My Name is Thread-0My Name is Thread-2线程的优先级分为10级,分别用1到10的整数代表线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:static int MAX_PRIORITY 线程可以具有的最高优先级。 static int MIN_PRIORITY 线程可以具有的最低优先级。 static int NORM_PRIORITY 分配给线程的默认优先级。 Java中的线程执行机制演示示例:线程的执行机制2其次是线程程
11、序本身的设计,比如使用sleep,yield,join,wait等方法(详情请看JDKDocument)Thread.sleep(longmillis );执行后观察其输出:Thread-0 睡了 13Thread-2 睡了 92Thread-1 睡了 94上面的执行结果是随机的,再执行很可能出现不同的结果。由于上面在run中添加了休眠语句,当线程休眠的时候就会让出cpu,cpu将会选择执行处于runnable状态中的其他线程,当然也可能出现这种情况,休眠的Thread立即进入了runnable状态,cpu再次执行它。Java中的线程执行机制演示示例:线程的执行机制3注意:线程睡眠是帮助其它线
12、程获得运行机会的最好方法。线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。sleep()是静态方法,只能控制当前正在运行的线程。Java中的线程执行机制Thread.yield()方法 Thread.yield()静态方法作用是:暂停当前正在执行的线程对象,并执行其他线程。yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达
13、到让步目的,因为让步的线程还有可能被线程调度程序再次选中。结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。Java中的线程执行机制演示示例:执行yield()方法执行结果:线程2第0次执行!线程1第0次执行!线程1第1次执行!线程1第2次执行!线程1第3次执行!线程1第4次执行!线程1第5次执行!线程1第6次执行!线程1第7次执行!线程1第8次执行!线程1第9次执行!线程2第1次执行!线程2第2次执行!线程2第3次执行!线程2第4次执行!线程2第5次执行!线程2第6次执行!线程2第7次执行!线程2第8
14、次执行!线程2第9次执行!Java中的线程执行机制Thread.join()方法将几个并行线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。 join为非静态方法,定义如下:void join()等待该线程终止。Java中的线程执行机制演示示例:执行Join()方法执行结果:主线程第0次执行!线程1第0次执行!主线程第1次执行!主线程第2次执行!主线程第3次执行!线程1第1次执行!线程1第2次执行!线程1第3次执行!线程1第4次执行!线程1第5次执行!线程1第6次执行!线程1第7次执行!线程1第8次执行!线程1第9次执行!主线程第4次执行!
15、主线程第5次执行!主线程第6次执行!主线程第7次执行!主线程第8次执行!主线程第9次执行!主线程第10次执行!主线程第11次执行!Java中的线程执行机制同步的提出:线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。线程的同步与锁演示示例:两个线程同时操作同一个对象运行结果:Thread-A : 当前foo对象的x值= 40Thread-B : 当前foo对象的x值= 40Thread-A : 当前foo对象的x值= -20Thread-B : 当前foo对象的x值= -20Thr
16、ead-B : 当前foo对象的x值= -80Thread-A : 当前foo对象的x值= -80从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。如果要保持结果的合理性,需要对Foo的访问加以限制,每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。线程的同步与锁使用同步锁synchronized关键字同步方法或代码语法:synchronized 可以修饰代码块,在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁例如:public int fix(int y) synchronized (this) x =
17、x - y; return x;线程的同步与锁接上 synchronized也可以作为一个method的修饰符非静态方法public synchronized int getX() return x+;等价于public int getX() synchronized(this)return x+;线程的同步与锁接上要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)对象。例如:public static synchronized int setName(String name) X = name;等价于public static int set
18、Name(String name) synchronized(Xxx.class) X = name; 线程的同步与锁注意:synchronized只能标记非抽象的方法,不能标识成员变量Java中每个对象都有一个内置锁当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁当程序运行到synchronized同步方法或代码块时该对象锁才起作用一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码
19、块,直到该锁被释放释放锁是指持锁线程退出了synchronized同步方法或代码块线程的同步与锁线程同步的运用运行结果:线程C运行结束,增加“-80”,当前用户账户余额为:20线程D运行结束,增加“-30”,当前用户账户余额为:-10线程E运行结束,增加“32”,当前用户账户余额为:22线程F运行结束,增加“21”,当前用户账户余额为:43线程B运行结束,增加“-60”,当前用户账户余额为:-17线程A运行结束,增加“20”,当前用户账户余额为:3线程的同步与锁演示示例:多个线程同时操作同一个对象线程同步的运用如果把上例子方法中的synchronized去掉运行结果:线程B运行结束,增加“-6
20、0”,当前用户账户余额为:-29线程F运行结束,增加“21”,当前用户账户余额为:3线程A运行结束,增加“20”,当前用户账户余额为:-29线程C运行结束,增加“-80”,当前用户账户余额为:3线程E运行结束,增加“32”,当前用户账户余额为:3线程D运行结束,增加“-30”,当前用户账户余额为:3 原因:多个线程并发访问了竞争资源u,并对u的属性做了改动。线程的同步与锁同步锁要点:只能同步方法,而不能同步变量和类;每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步不必同步类中所有的方法,类可以同时拥有同步和非同步方法。如果两个线程要执行一个类中的synchro
21、nized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。线程的同步与锁接上:如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。线程睡眠时,它所持的任何锁都不会释放。 线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。同步损害并发性,应该尽可能缩小同步范围。线程的同步与锁线程不能获得锁会怎么样如果线程试图进入同步方法,而其锁已经被占用,则线程在该对
22、象上被阻塞。实质上,线程进入该对象的的一种池中,必须在那里等待,直到其锁被释放,该线程再次变为可运行或运行。当考虑阻塞时,一定要注意哪个对象正被用于锁定:调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。 调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。线程的同步与锁线程死锁死锁对程序来说,是很复杂的,也很难发现问题。当两个线程被
23、阻塞,每个线程在等待另一个线程时就发生死锁。实际上发生死锁的概率很小,但是,无论代码中发生死锁的概率有多小,一旦发生死锁,程序就死掉。有一些设计方法能帮助避免死锁,包括始终按照预定义的顺序获取锁这一策略等。线程的同步与锁演示示例:线程死锁线程同步小结 线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。线程同步方法是通过锁来实现,每个对象都有且仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中
24、访问另外对象上的同步方法时,会获取这两个对象锁。对于同步,要时刻清醒在哪个对象上同步,这是关键。线程的同步与锁接上:编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小,但是,一旦程序发生死锁,程序将死掉。线程的同步与锁线程交互线程交互知识涉及到java.lang.Object的类的三个方法:void wait() 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。void notify() 唤醒在此对象监视器上等待的单个线程。 void notifyAll() 唤醒在此对象监视器上等待的所有线程。线程的交互 例子:当在b对象上调用wait()
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论