Java核心技术-并发

这一章要了解的:

  • 什么是线程(创建线程的两种方式,推荐使用Runnable接口的方式

  • 中断线程(注意interrupted和isInterrupted的区别)

  • 线程的状态(6种状态)

  • 线程属性(4个)

  • 同步***(锁对象 条件对象 synchronized关键字 同步阻塞)

  • 阻塞队列(重点理解队列

  • 线程安全的集合

  • Callable与Future

  • 执行器

  • 同步器

  • 线程与Swing

*一个程序可以执行多个任务,通常,每一个任务称为一个线程,它是线程控制的简称*(想象一下QQ,可以接收消息的同时发送消息)

*可以同时运行一个以上线程的程序称为多线程程序*

**多线程与多进程的区别:本质区别在于每个进程拥有自己的一整套变量,而线程则是共享数据;线程更“轻量级”,创建,撤销一个线程比启动新进程的开销要小得多

static void sleep(long millis):休眠给定的毫秒数:不会创建新的线程,而是用于暂停当前线程的活动

一 什么是线程

1 使用线程给其他任务提供机会

 **如果需要执行一个比较耗时的任务,应当并发地运行任务**

在一个单独的线程中执行一个任务的简单过程:

  1. 将任务代码一道实现了Runnable接口的类(方法一)的run方法中

public interface Runnable{

void run();

}

可以用lambda表达式创建一个接口的实例:

Runnable r = ()->{要执行的任务代码};

     2.由Runnable创建一个Thread对象

Thread t = new Thread(r);

    3.启动线程

t.start();

也可以通过构建一个Thread类的子类定义一个线程(方法二,但是这种方法不再推荐,可以使用线程池来解决问题)

class MyThread extends Thread{

public void run();

}

ps:直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程;应该调用Thread.start方法,这个方法将创建一个执行run方法的新线程


 

二 中断线程

1 没有强制线程终止的方法。interrupt方法可以用来请求终止线程

2 当对一个线程调用interrupt方法时,线程的中断状态将被置位。要想弄清楚是否被置位,应该:

while(!Thread.currentThread().isInterrupted()&&其他限制){

要做的工作

}

ps:如果线程被阻塞,就无法检测中断状态,这是产生InterrruptedException异常的地方

3 每个线程都应该不时地检查中断状态这个标志,以判断线程是否被中断

4 interrupted和isInterrupted的比较:interrupted是一个静态方法,它检测当前线程是否被中断。而且调用interrupted方法会清除该线程的中断状态

    isInterrupted是一个实例方法,可用来检验是否有线程被中断,调用isInterrupted方法不会改变中断状态

常用方法:java.lang.Thread

  • void interrupt()向线程发送中断请求,线程的中断状态被置为true,如果目前该线程被一个sleep调动阻塞,那么InterruptedException异常被抛出

  • static boolean interrupted()测试当前线程是否被中断,它将当前线程的中断状态重置为false

  • boolean isInterrupted()测试线程是否被终止,这一调用不改变线程的中断状态

 


 

三 线程状态

线程的6种状态:

  1.  new(新创建)

  2. Runnable(可运行)

  3. Blocked(被阻塞)

  4. Waiting(等待)

  5. Timed waiting(及时等待)

  6. Terminated(被终止)

1 新创建线程

使用new操作符 ,new Thread(r)

当一个线程处于新创建状态时,程序还没有运行线程中的代码,在线程还没有运行之前还要做一些基础工作

2 可运行线程

一旦调用start方法,线程处于runnable状态

对于一个可运行的线程可能正在运行也可能没有运行

3被阻塞线程和等待线程

  • 当一个线程试图获取一个内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态

只有当所有其他线程释放该锁,并且线程调度器允许本县城持有它的时候,该线程变成非阻塞状态

当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态

4 被终止的线程

1 线程被终止的原因:

  • 因为run方法正常退出而自然死亡(自然死亡)

  • 因为一个没有捕获的异常终止了run方法而意外死亡(非自然死亡)

常用方法:java.lang.Thread

  • void join()等待终止指定的线程

  • void join(long millis)等待指定的线程死亡或者经过指定的毫秒数

  • Thread.State getState()得到这一线程的状态


     

四 线程属性(线程优先级    守护线程    处理未捕获异常的处理器    线程组)

1 线程的优先级:默认情况下,一个线程继承它的父线程的优先级,可以使用setPriority方法提高或降低任何一个线程的优先级

常用方法:java.lang.Thread

  • void setPriority(int new Priority)设置线程的优先级(1-10)

  • static void yield():导致当前线程处于让步状态

2 守护线程:t.setDaemon(true)

用途:为其他线程提供服务

守护线程应该永远不去访问固有资源

3 未捕获异常处理器:线程的run方法不能抛出任何受查异常

该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类,这个接口只有一个方法

void unCaughtException(Thread t,Throwable e)

安装线程处理器的方法

  1. 用setUncaughtExceptionHandler方法为任何线程安装一个处理器

  2. 用Thread类的静态方法setDefaultUnCaughtExceptionHandler为所有线程安装一个默认的处理器

如果不安装默认的处理器,则默认的处理器为空。但是如果不为独立的线程安装处理器,此时的处理器就是线程的ThreadGroup对象

线程组:线程组是一个可以统一管理线程的集合。默认情况下,创建的所有线程属于相同的线程组,建议不要在自己的程序中使用

ThreadGroup类实现Thread.UncaughtExceptionHandler接口,它的uncaughtException方法操作流程

  1. 如果该线程有父线程组,那么父线程组的uncaughtException方法被调用

  2. 否则,如果Thread.getDefaultExceptionHandler方法返回一个非空的处理器(有默认处理器),则调用该处理器

  3. 否则,输出栈轨迹到标准错误输出流上

好例子https://blog.youkuaiyun.com/hongxingxiaonan/article/details/50527169


 

五 同步

1 没有实现同步的例子

2 竞争条件:需要保证线程在失去控制之前方法运行完成,就能保证并发访问的干扰

3 用锁对象来保证并发访问的干扰的两种方法:

  • 使用synchronized关键字

  • 使用ReentrantLock保护代码块

myLock.lock();//调用ReentrantLock实例化 对象

try{

 

}finally

{

myLock.unlock();//必须将解锁操作放在finally子句之内,否则线程将永远阻塞

}

这一结构确保任何时刻只有一个线程进入临界区。一旦一个线程封锁了锁对象,其他任何线程都无法通过lock语句。当有其他线程调用lock时,它们将处于阻塞状态,直到第一个线程释放锁对象

锁时可以重入的,因为线程可以重复地获取已经持有的锁

锁保持一个持有计数来跟踪对lock方法的嵌套调用,被一个锁保护的代码可以调用另一个使用相同的锁的方法

补充:什么是临界区?同一时刻只能有一个任务访问的代码区

WARNNING:要留心临界区中的代码,不要因为异常的抛出而跳出临界区

条件对象:用来管理那些已经获得了一个锁但是却不能做有用工作的线程(用来管理线程的)

使用锁来保护检查与转账动作来确保没有其他线程在本检查余额与转账活动之间修改金额:

public void transfer(int from,int to,int amount){

bankLock.lock();

try{

while(accounts[from]<amount){

//wait 保证bankLock的排他性访问

}

}

finally{

bankLock.unlock();

}

}

一个锁可以有一个或多个条件对象

 使用newCondition方法获得一个条件对象

class Bank{

private Condition sufficientFunds;

public Bank(){

...sufficientFunds = bankLock.newCondition();

}

}

等待获得锁的线程和调用 await 方法的线程存在本质的不同:

  • 1.4.1)一旦一个线程调用 await 方法, 它进入该条件的等待集, 当锁可用时,该线程不能马上解除阻塞;

  • 1.4.2)相反,它处于阻塞状态, 直到另一个线程调用同一个条件上的signalAll 方法为止, 这一调用重新激活 因为这一条件而等待的所有线程;

  • 1.4.3)一旦锁成为可用的, 它们中的某个将从 await 方法调用返回, 获得该锁并从阻塞的地方继续执行;

signalAll方法的作用:它仅仅是通知正在等待的线程,此时有可能已经满足条件,值得再次去检测该条件

死锁现象的发生:当一个线程调用await时,它没有办法重新激活自身,只能寄希望于其他线程,如果没有其他线程重新激活等待的线程,它就永远不再运行了,该程序就被挂起了

何时调用signalAll方法:在对象的状态有利于等待线程的方向改变时调用

public void transfer(int from,int to,int amount){

bankLock.lock();

try{

while(accounts[from]<amount)

sufficient.await();

...转账业务

sufficientFunds.singleAll();//不会立即激活一个等待线程,只是解除等待线程的阻塞,以便这些线程可以在当前线程退出同步方法之后,通过竞争实现对对象的访问

finally{

bankLock.unlock();

}

}

single方法: 则是随机解除等待集中某个线程的阻塞状态,这比解除所有线程的阻塞更加有效, 但也存在危险,容易发生死锁

5 synchronized关键字

锁和条件的关键之处:

  • 锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码

  • 锁可以管理试图进入被保护代码段的线程

  • 锁可以拥有一个或多个相关的条件对象

  • 每个条件对象管理那些已经进入被保护的代码段但还不能运行的线程

如果一个方法用synchronized关键字声明,那么对象的锁将保护整个方法,要调用该方法,线程必须获得内部的对象锁

内部对象锁只有一个相关条件,wait方法添加一个线程到等待集中,notify/notifyAll方法解除等待线程的阻塞状态

调用wait和notifyAll等价于await和singleAll

条件对象.await();

条件对象.singleAll();

使用 synchronized关键字来编写代码简洁很多,每一个对象有一个内部锁,并且锁有一个内部条件,由锁来管理那些试图进入synchronized方法的线程,由条件来管理那些调用wait的线程

静态方法声明为synchronized也是合法的,该方法获得相关的类对象的内部锁;若当该方法被调用时,该对象的锁被锁住,因此没有其他线程可以调用同一个类的这个或者其他的同步静态方法

内部锁和条件存在一些局限,包括:

  • 不能中断一个正在试图获得锁的线程

  • 试图获得锁时不能设定超时

  • 每个锁仅有单一的条件,可能是不够的

Lock Condition 同步方法如何选择:

  • 最好既不使用Lock/Condition(条件对象)也不使用synchronized关键字

  • 尽量使用synchronized关键字

  • 除非特别想使用Lock/Condition结构提供的独有特性时,才使用它

补充:sleep wait的区别:

  • sleep方法必须传入参数,参数就是时间,时间到了自动醒来;wait方法不传参数就直接等待,传参数就在指定的时间结束后等待

  • sleep方法在同步函数或同步代码块中不释放锁;wait方法释放


     

六 阻塞队列(可以理解为一种同步机制)

1当试图从队列添加元素而队列已满,或是想从队列移出到元素而队列为空的时候,阻塞队列导致线程阻塞。

2队列会自动的平衡负载,如果第一个线程集运行的比第二个慢,第二个线程在等待结果时会阻塞;如果第一个线程集运行的快,它将等待第二个队列集赶上来

3阻塞队列方法分为三类,取决于它们的相应方式:

  1. 如果将队列当做线程管理工具来使用,将用到put和take方法

  2. 当试图从满的队列中添加或从空的队列中移除元素时,add,remove,element操作抛出异常

  3. 在多线程程序中,队列会在任何时候空或满,所以一定要使用offer(添加一个元素并返回true),poll(移除并返回队列的头元素),peak(返回队列的头元素)方法来替代,这些方法不会抛出异常

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在软件设计里到处都是模式,框架。有次朋友问什么是模式?我也在学习中,就我的学习经验,给出以下小结。(注意:个人观点,仅供参考,欢迎指正。)??1.什么是模式???模式,即pattern。其实就是解决某一类问题的方法论。你把解决某类问题的方法总结归纳到理论高度,那就是模式。??Alexander给出的经典定义是:每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。??模式有不同的领域,建筑领域有建筑模式,软件设计领域也有设计模式。当一个领域逐渐成熟的时候,自然会出现很多模式。??什么是框架???框架,即framework。其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。??2.为什么要用模式???因为模式是一种指导,在一个良好的指导下,有助于你完成任务,有助于你作出一个优良的设计方案,达到事半功倍的效果。而且会得到解决问题的最佳办法。??为什么要用框架???因为软件系统发展到今天已经很复杂了,特别是服务器端软件,设计到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮你完成一些基础工作,你只需要集中精力完成系统的业务逻辑设计。而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事物处理,安全性,数据流控制等问题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。??框架一般处在低层应用平台(如J2EE)和高层业务逻辑之间的中间层。??软件为什么要分层???为了实现“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源…总之好处很多啦:)。??3.以下所述主要是JAVA,J2EE方面的模式和框架:??常见的设计模式有什么???首先,你要了解的是GOF的《设计模式--可复用面向对象软件的基础》一书(这个可以说是程序员必备的了),注意:GOF不是一个人,而是指四个人。它的原意是Gangs Of Four,就是“四人帮”,就是指此书的四个作者:Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides。这本书讲了23种主要的模式,包括:抽象工厂、适配器、外观模式等。??还有其他的很多模式,估计有100多种。??软件设计模式太多,就我的理解简单说一下最常见的MVC模式。??MVC模式是1996年由Buschmann提出的:??模型(Model):就是封装数据和所有基于对这些数据的操作。??视图(View):就是封装的是对数据显示,即用户界面。??控制器(Control):就是封装外界作用于模型的操作和对数据流向的控制等。??另外:??RUP(Rational Unified Process)软件统一过程,XP(Extreme Programming)极端编程,这些通常被叫做“过程方法”,是一种软件项目实施过程的方法论,它是针对软件项目的实施过程提出的方法策略。也是另一个角度的模式。??4.常见的JAVA框架有什么???WAF:??全称:WEB APPLICATION FRAMEWORK??主要应用方面:EJB层,(WEB层也有,但是比较弱)。??主要应用技术:EJB等??出处:http://java.sun.com/blueprints/code/index.html??简述:这是SUN在展示J2EE平台时所用的例子PetStore(宠物商店系统)里面的框架。是SUN蓝皮书例子程序中提出的应用框架。它实现了 MVC和其他良好的设计模式。SUN的网站上有技术资料,最好下载PetStore来研究,WEBLOGIC里自带此系统,源码在beaweblogic700samplesserversrcpetstore。这是学习了解J2EE的首选框架。??免费。??Struts:??主要应用方面:WEB层。??主要应用技术:JSP,TagLib,JavaBean,XML等??出处:http://jakarta.apache.org/struts/index.html??简述:这是APACHE的开源项目,目前应用很广泛。基于MVC模式,结构很好,基于JSP。Jbuilder8里已经集成了STRUTS1.02的制作。??免费。??简述WAF+STRUTS结合的例子:WEB层用STRUTS,EJB层用WAF:??JSP(TagLib)——>ActionForm——>Action ——> Event——>EJBAction——>EJB ——>DAO——>Database JSP(TagLib)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值