Java多线程总结

时间:2024.4.14

1.线程的创建与启动

在Java中,多线程的实现有两种方式:
继承java.lang.Thread类
实现java.lang.Runnable接口

1.1继承Thread类创建线程

继承Thread类创建并启动线程的步骤:
1、定义Thread的子类,并重写该类的run()方法,run()方法的方法体就表示线程需要完成的任务。run()被称为线程执行体。
2、创建Thread的子类的实例,即创建线程对象。
3、调用线程对象的start()方法启动该线程。

1.2实现Runnable接口创建线程

实现Runnable接口创建并启动线程的步骤:
1、定义Runnable接口的实现类,并重写该接口的run()方法,run()方法的方法体就表示线程需要完成的任务。run()被称为线程执行体。
2、创建Runnable的实现类的实例,并以此实例作为Thread的target来实现Thread对象,该Thread对象才是真正的线程对象。
3、调用线程对象的start()方法启动该线程。
PS:使用Runnable创建的多个线程对象可以共享Runnable的实现类的实例属性。

2.线程的生命周期

在线程的生命周期中有五种状态。

2.1新建状态和就绪状态

当程序使用new关键字创建一个线程之后,该线程就处于新建状态。
当线程对象调用了start()方法之后,该线程就处于就绪状态。处于就绪状态的线程并没有开始运行,只是可以运行而已,,至于该线程何时开始运行就取决于JVM里线程调度器的调度。
PS:不能直接调用线程的run()方法,不然就变成了普通方法的调用,而不是执行线程执行体。

2.2运行状态和阻塞状态

处于就绪状态的线程获得了CPU,开始执行run()方法的方法体,则该线程处于运行状态。
当一个线程开始运行后,在某个时刻失去了CPU,则该线程处于阻塞状态。
当发生如下情况时,线程会进入阻塞状态(从运行状态转入阻塞状态):
1、线程调用sleep()方法主动放弃占用的处理器资源
2、线程调用了一个阻塞式IO方法
3、线程试图获取一个同步监视器,但是该同步监视器正被其他线程所拥有。
4、线程在等待某个通知
5、线程调用suspend()方法将该线程挂起。这个方法容易造成死锁。
针对上面几种情况,当发生如下情况时可以解除阻塞(从阻塞状态转入就绪状态):
1、调用sleep()方法的线程过了指定的时间
2、线程调用的阻塞式IO方法已经返回
3、线程成功获取它试图获取同步监视器
4、线程正在等待某个通知时,其他线程发出了一个通知
5、处于挂起状态的线程调用了resume()方法
PS:线程调用yield()方法可以从运行状态转入就绪状态

2.3死亡状态

线程会以三种方式结束,结束后就处于死亡状态:
1、线程的run()执行完成,线程正常结束
2、线程抛出一个未捕获的Exception或Error
3、线程调用stop()方法来结束该线程,该方法容易造成死锁
为了测试某个线程是否死亡,可以调用线程对象的isAlive()方法,当线程处于就绪,运行,阻塞三种状态时,返回true,当线程处于新建,死亡两种状态时,返回false。

3.线程控制 

Java的线程提供一些便捷的工具,通过这些工具可以很好地控制线程的执行。

3.1join线程

Thread提供让一个线程等待另一个线程完成的方法——join()方法。当在某个线程的执行过程中调用另一个线程的join()方法时,调用线程将被阻塞,直到被join()方法的线程执行完为止。

3.2后台线程

有一种线程是在后台运行的,它的任务是为其他线程提供服务,这种线程叫做后台线程(Daemon Thread),又称为守护线程或精灵线程。
后台线程有个十分重要的特点:如果所有的前台线程死亡,那么后台线程自动死亡。
调用线程对象的setDaemon(true)方法可将指定线程设置为后台线程。
Thread类还提供一个isDaemon()方法,用来判断指定线程是不是后台线程,是后台线程就返回true,否则返回false。
主线程(main线程)默认是前台前程。
前台,后台线程有一个重要的特点:前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认是后台线程。
PS:setDaemon(true)设置线程为后台线程必须在start()方法之前调用。

3.3线程睡眠

如果需要让当前正在后执行的线程暂停一段时间,并进入阻塞状态,可以通过调用Thread类的静态sleep()方法实现。

3.4线程让步

yield()方法是一个和sleep()方法相似的方法,它也是Thread类的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是让该线程转入就绪状态。
当某个线程调用了yield()方法暂停后,只有优先级与该线程相同,或者优先级高于该线程的处于就绪状态的线程才会获得执行的机会。

3.5线程的优先级

每个线程都具有一定的优先级,优先级高的线程有更多的机会获得执行,优先级低的有更少的机会获得执行。
主线程(main线程)默认优先级是普通优先级,每个线程的默认优先级都与创建它的父线程相同。
Thread类提供setPriority(int newPriority)方法和getPriority()方法来设置和获取指定线程的优先级,其中setPriority(int newPriority)方法中的参数取值范围是1-10,也可以使用Thread类的三个静态常量
MAX_PRIORITY:值是10
MIN_PRIORITY:值是1
NORM_PRIORITY:值是5
PS:Java提供10个线程优先级,但不是所有的操作系统都支持这10个优先级,所以我们应该尽量避免直接有数字来设置优先级,应该多用三个静态常量来设置优先级,这样程序的移植行更好。

4.线程同步

4.1线程安全问题

多个线程同时访问一个对象时,可能会出现线程安全问题

4.2同步监视器

为了解决线程安全问题,Java的多线程支持引入了同步监视器。

4.2.1同步代码块

使用同步监视器的一个方法是同步代码块。
synchronized(obj){
...//此处代码就是同步代码块
}
synchronized后面圆括号里面的obj就是同步监视器,上面代码的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。
任何时刻只能有一个线程获取对同步监视器的锁定。

4.2.2同步方法

使用同步监视器的一个方法是同步方法。
对同步方法而言,无须显示地指定同步监视器,同步方法的同步监视器是this,也就是同步方法本身。
public synchronized void play(){
...//同步方法体
}
使用同步方法可以非常方便地实现线程安全类,线程安全类具有以下特征:
1、该类的对象可以被多个线程安全访问
2、每个线程调用该对象的任意方法后将得到正确的结果
3、每个线程调用该对象的任意方法后,该对象依然保持合理状态

4.2.3释放同步监视器的锁定

释放同步监视器的锁定的方法:
1、当前线程的同步方法、同步代码块执行结束
2、当前线程的同步方法、同步代码块中遇到break、return终止了该方法、该代码块的继续执行
3、当前线程的同步方法、同步代码块中出现了未处理的Error或Exception,导致该方法、该代码块异常结束
4、当前线程执行同步方法、同步代码块时,程序执行了同步监视器对象的wait()方法,则当前线程暂停
如遇到以下几种情况,线程不会释放同步监视器
1、当前线程执行同步方法、同步代码块时,程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行
2、当前线程执行同步代码块时,其他线程调用该线程的suspend()方法将该线程挂起。这个方法容易造成死锁。

4.3同步锁

从Java5开始,Java提供了一种功能强大的心爱你策划给你同步机制——通过显示定义同步锁对象来实现同步,在这种机制下,同步锁使用Lock对象充当。
Lock提供比同步方法、同步代码块更广泛的锁定操作,Lock实现允许更灵活的结构,可以具有差别很大的属性,并且支持多个多个相关的Condition对象。
Lock是充值多线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
某些锁可能允许对共享资源并发访问,如ReadWriteLock(读写锁)。
Lock、ReadWriteLock是Java5提供的两个根接口,并为Lock提供了ReentrantLock(可重入锁)实现类,为ReadWriteLock提供了ReentrantReadWriteLock实现类。
使用ReentrantLock的常用格式如下

[java] view plaincopy

1.  import java.util.concurrent.locks.ReentrantLock;  

2.    

3.  public class LockTest {  

4.      private final ReentrantLock lock=new ReentrantLock();  

5.      public void play(){  

6.          lock.lock();  

7.          try{  

8.              //需要保证线程安全的代码                     

9.          }finally{  

10.             lock.unlock();  

11.         }  

12.     }  

13. }  


ReentrantLock锁具有可重入性,也就是说,一个线程可以对已经被加锁的ReentrantLock锁再次加锁,一段被锁保护的代码可以调用另一个被相同锁保护的方法。

4.4死锁

当两个线程互相等待对方释放同步监视器时,就会出现死锁。所以多线程编程时要采取措施避免死锁。

5.线程通信

当线程在系统内运行时,线程的调度具有一定的透明性,程序通常无法准确控制线程的轮换执行,但我们可以通过一些机制来保证线程协调运行。

5.1传统的线程通信

为了实现线程间的通信,可以借助Object类提供的wait()、notify()、notifyAll()3个方法,但这3个方法必须有同步监视器对象来调用。
1、对于使用synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以在同步对象中直接调用这3个方法
2、对于使用synchronized修饰的同步代码块,同步监视器是synchronized后括号里的对象,所以必须使用该对象调用这3个方法
关于这3个方法的含义
1、wait()方法:导致当前线程等待,知道其他线程调用该同步监视器的notify()方法或notifyAll()方法
2、notify()方法:唤醒在此同步监视器上等待的单个线程
3、notifyAll()方法:唤醒在此同步监视器上等待的所有线程

5.2使用Condition控制线程通信

如果程序不使用synchronized关键字来保证同步,而直接使用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也不能使用前面的3个方法来进行通信了。
当使用Lock对象来保证同步时,Java提供一个Condition类来保证协调,使用Condition可以让那些得到Lock对象却无法继续执行的线程释放Lock对象,Condition对象也可以唤醒其他处于等待的线程。
这时,Lock替代了同步方法或同步代码块,Condition替代了同步监视器的功能。
Condition实例被绑定在一个Lock对象上。要获取特定Lock实例的Condition实例,调用Lock对象的newCondition方法即可。Condition提供了如下3个方法。
1、await()方法:类似于隐式同步监视器上的wait()方法,导致当前小昵称等待,直到其他线程调用该Condition的signal()方法或signalAll()方法来唤醒该线程
2、signal()方法:唤醒在此Lock对象上等待的单个线程
3、signalAll()方法:唤醒在此Lock对象上等待的所有线程

5.3使用阻塞队列控制线程通信

Java5提供了一个BlockingQueue接口,虽然它也是Queue的子接口,但它的主要作用不是作容器,而是作为线程同步的工具。BlockingQueue具有一个特征:当生产者线程试图向BlockingQueue中加入元素时,如果队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取出元素时,如果队列已空,则该线程被阻塞。程序的两个线程通过交替向BlockingQueue中放入元素、取出元素,就可以很好地控制线程的通信了。
BlockingQueue提供如下两个支持阻塞的方法,
1、put(E e)方法:尝试把E元素放入BlockingQueue中
2、take()方法:尝试从BlockingQueue的头部取出元素


第二篇:多线程总结


小乐多线程经验交流

本章是重点,当你在写系统一点的项目时,线程控制是必不可少的,比如我在写QQ聊天软件雏形那个小项目时,我就想让多个客户端可以通话,要怎么才能实现呢?我就在Client端与Server端专门用一个线程来控制信息的相互传递。首先,在Server端,我写了一个接收每一个Client端发过来的会话并将它们发过来的会话发送给每一个客户端的线程,在Client端写一个接收Sever端发送过来的会话并将接收到的会话打印在TextArea里面做展现的线程, 这样就可以像QQ聊天软件那样开始聊天了,只是界面太过简单,一点都不漂亮,但主要是实现功能,当然不要多线程也是可以开发QQ的,用Java特有的异步模型也是可以解决的。

好了,下面我就具体分析一下多线程吧,从概念讲起,什么是多线程呢?大家平时用电脑的时候有没有注意过,你在一边聊QQ的时候还能看电影,还能打游戏,其实Windows是支持多进程的,linux和Unix也是支持多进程的,谁不支持呢,Dos不支持多进程,进程与线程有什么区别呢?其实两者也没什么本质的区别,进程往大了说,线程往小了说。好了,不抠这些细节了,也没什么意思,

翻开Api文档可以知道多线程这一章有start方法,stop方法,sleep方法,notify方法,notifyAll方法还有run方法,当你要执行某一线程的时候首先要让这个线程首先的start,然后才能执行你定义的线程内部的run方法,sleep方法是让线程睡眠多少毫秒数,会抛InterruptedException线程被打断异常,stop 方法和interrpted方法都是打断线程的方法,但是stop方法比较粗鲁,咣当一棒打你脑门上看你这个线程死不死。Notify和notifyAll方法是叫醒正在睡眠的那一个线程,咣当一盆凉水泼你脸上看你还敢不敢睡,然后线程要不得不继续干活了,太霸道了。

下面谈一下多线程这一章的生命周期的问题,线程也有生死,就像人的生命周期差不多,人也是有生有死的,昨天看了篇文章就是谈生死的,看完后我就慨叹人生短暂呀,得节约时间

多线程总结

多线程总结

Start()方法

看上面我画的这个图,当你创建好了一个线程之后,首先调用start方法进入就绪状态,就好比你肚子有反应了之后放下手上的工作就去厕所了一样,当你进去了,你就进入就绪状态了,线程进入就绪状态之后就开始进入运行状态,执行run方法里面的内容,就好比你进入厕所,找着坑位准备办事一样,忽然间,线程在运行的时候抛异常了,这就进入了阻塞状态,在catch到那个异常之后做出相应的处理,当你在厕所里办事的时候,办着办着一拍脑门,哎呀,忘带手纸了,你没手纸就等于抛异常了,你就没办法出来呀,总不能不擦屁股就出来吧,咱们都是文明人,不做这事,这也太呕心了吧,所以你还是老老实实先待着准备别人来给你送手纸吧,线程抛出来的异常被解决了之后,又进入了就绪状态,就好比你找了个人给你送来了手纸你就可以完事出来了,当然了,如果你还想办事,OK,你脱裤子继续好了,没人拦着你。

? Sleep方法

? 可以调用Thread的静态方法:

? public static void sleep(long millis)throwsInterruptedException

? 使得当前线程休眠(暂时停止执行millis毫秒)。

? 由于是静态方法,sleep可以由类名直接调用:

? Thread.sleep(…)

? Join 方法

? 合并某个线程

? Yield 方法

? 让出CPU,给其他线程执行的机会

以上的方法中,你可能不容易理解join方法和Yield方法,Join方法是合并某个线程,将当前线程和主线程合并,一起执行,没有分支了,等当前线程执行完了之后才执行主线程。如果又来个线程,优先级特高,就先执行优先级高的线程,就好比你跟你的一个朋友一起去厕所办事,忽然间,所长来了,发现坑位已经满了,叫你和你的朋友合并一个,你两共用一个坑,呵呵,你真不幸呀,没办法,人家优先级比你高,忍着吧。告状都没地方告去,Java就这么规定的。下面再谈一下什么是yield方法,这个方法让出CPU,给其他线程执行的机会,就接着刚才的例子继续说吧,又有一天,你和你同学去厕所办事,发现就一个坑,你心地停善良的,就哥们义气地说:你先上吧,我先憋会,里面有个人,心特好,他对你说,兄弟,看你停急的,让你先蹲会吧,不然憋坏了怎么办呀,于是,他就起来,让出坑位让你先执行一小会,然后说,你还是先出来一会,我快憋不住了,先让我执行一会,然后再让给你,于是,你和他就你执行一会他执行一会,这就是Yield方法。

下面重点谈一下线程同步的问题,用线程同步来解决死锁问题。

? 在java语言中,引用了对象互斥锁的概念,保证共享数据操作的完整性。每个对象

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

? 关键字synchronized来与对象的互斥锁联系。当某个对象被synchronized修饰时,表

明该对象在任一时刻只能由一个线程访问。

? Synchronized的使用方法

? Synchronized还可以放在方法声明中,表示整个方法为同步方法。

什么是线程同步呢,简单的说就是几件事情同时进行,比如你和你老婆这个月和辛苦,赚了不少钱,今天正好两个人都有空,两个人一商量决定去逛商场,于是两个人准备去银行提钱,一开始没说谁提,只是说在哪集合的,于是你带着银行卡就去银行取钱去了,你将卡向里面一插,在交易处理中的时候,你老婆刚好也在银行取钱,她拿着你们家的存折去银行取钱,一共存在银行就3000块钱,结果两个人每人取了两千块钱,太郁闷了,可是这是不可能的,银行没那么傻,他肯定是用synchronized定义成同步式的取钱方法了,你取钱,你取完了,你老婆才能取,

那什么是死锁呢,比如说,你和你老婆吵架,关系不好了,吃饭的时候只有一双筷子,你们两个一人一支,这时候你拿着左手这根筷子等着右手这支筷子,你老婆拿着右手这支筷子等着左手这支筷子,结果两人谁也不让谁,结果就是看着满桌子的鸡鸭鱼肉被活活饿死了。

这就是线程死锁问题,在锁定线程的时候,别几个对象都同时锁定,要将锁的粒度加大。

更多相关推荐:
Java多线程总结

基本概念:并发性和并行性:并发性指的是同一时刻只能有一条指令执行,但是多个进程指令被快速轮换执行并行性是指同一时刻,有多条指令在多个处理器上同时执行,使得在宏观上具有多个进程同时执行的结果临界区:修改共享资源的…

java多线程总结(含代码)

Java多线程总结Java多线程总结目标:理解程序、线程和进程的概念理解多线程的概念掌握线程的各种状态熟练使用Thread类、Runnable接口创建线程熟练使用线程各种方法1、程序、线程、进程进程:调度和和独…

Java多线程总结

Java多线程总结线程的四种状态:就绪Runable——运行中Running——阻塞Blocked——死亡Dead(结束)线程的状态转换图线程在一定条件下,状态会发生变化。线程变化的状态转换图如下:1、新建状态…

Java多线程总结

多线程总结在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,更多Java学习,请登陆疯狂java官网。1.重写线程Thread,Runnable…

Java多线程编程总结

Java线程概念与原理一操作系统中线程和进程的概念现在的操作系统是多任务操作系统多线程是实现多任务的一种方式进程是指一个内存中运行的应用程序每个进程都有自己独立的一块内存空间一个进程中可以启动多个线程比如在Wi...

Java多线程总结

Java多线程编程总结Java语言的一个重要特点是内在支持多线程的程序设计多线程是指在单个的程序内可以同时运行多个不同的线程完成不同的任务多线程的程序设计具有广泛的应用本章主要讲授线程的概念如何创建多线程的程序...

java多线程总结

java多线程总结java中的多线程在java中要想实现多线程有两种手段一种是继续Thread类另外一种是实现Runable接口对于直接继承Thread的类来说代码大致框架是class类名extendsThre...

Java多线程编程总结

Java多线程编程总结20xx0517112159标签java多线程原创作品允许转载转载时请务必以超链接形式标明文章原始出处作者信息和本声明否则将追究法律责任httplavasoft6257527069Java...

Java线程总结

Java线程总结首先要理解线程首先需要了解一些基本的东西我们现在所使用的大多数操作系统都属于多任务分时操作系统正是由于这种操作系统的出现才有了多线程这个概念我们使用的windowslinux就属于此列什么是分时...

Java多线程学习总结

Java多线程学习总结1线程状态总的可分为五大状态分别是生死可运行运行等待阻塞2threadstart进入可运行状态threadsleep进入睡眠状态1waitnotifynotifyAll是三个定义在Obje...

Java_多线程与并发编程总结

Java多线程与并发编程总结认识多任务多进程单线程多线程要认识多线程就要从操作系统的原理说起以前古老的DOS操作系统V622是单任务的还没有线程的概念系统在每次只能做一件事情比如你在copy东西的时候不能ren...

Java多线程编程总结

Java线程概念与原理一操作系统中线程和进程的概念现在的操作系统是多任务操作系统多线程是实现多任务的一种方式进程是指一个内存中运行的应用程序每个进程都有自己独立的一块内存空间一个进程中可以启动多个线程比如在Wi...

java多线程总结(31篇)