转载请申明出处:http://blog.csdn.net/xmxkf/article/details/9945499

01. 传统线程技术回顾

创建线程的两种传统方式:

1、在Thread子类覆盖的run方法中编写运行代码;

涉及一个以往知识点,能否在run方法声明上抛出InterruptedException异常?以便省略run方法内部对Thread.sleep()语句的try……catch处理?

答:不能,因为Thread类的run方法没有抛异常,子类覆盖run方法时也不能抛异常。

2、在传递给Thread对象的Runnable对象的run方法中编写代码;

Thread thread1 =new Thread(){

@Override

publicvoid run()

{

while(true)

{

try {

Thread.sleep(500);

} //不能让run方法throws异常,因为父类run没有抛异常

catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName());//this.getName()也行

}

}

};

thread1.start();

//创建Thread对象,并传入一个Runnble对象到构造方法

Thread thread2 =
new
Thread(new Runnable(){

@Override

publicvoid run()

{

while(true)

{

try {

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

//此处就不能用this.getName,因为this代表Runnable子类对象

System.out.println(Thread.currentThread().getName());

}

}

});

这两种方式有什么区别?为什么通常人们采用第二种(实现Runnable接口)?

都是创建一个线程,只是第二种方式更加体现面向对象的思想;创建一个线程,线程要运行的代码封装到Runnable对象中(run方法)。

3、总结:查看Thread对象的run方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖(不是继承Thread)并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。

Public void run()

{

if(target != null)

{//当Thread构造方法中传递了Runnable对象就执行Runnable的run方法。

target.run();

}

}

4、问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么,线程运行时的执行代码是子类的run方法的代码还是Runnable对象的run方法的代码?

答:(Thread子类的run),因为子类覆盖了父类的run方法,子类对象调用start方法时,会到自己的类中找run方法,如果没有找到就会执行父类的run方法,而父类(Thread)的run方法中会去调用Runnable对象的run方法。只要覆盖了Thread中的run方法,就不会执行Runnable对象的run方法。

涉及到一个以往的知识点:匿名内部类对象的构造方法如何调用父类的非默认构造方法。

5、多线程机制会提高程序的运行效率吗?为什么会有多线程下载呢?

答:多线程不会提高程序的运行效率甚至更慢,因为cpu同一时间只能执行一个线程,如果要在几个线程之间切换需要时间。

计算机并没有快,只是抢了服务器的带宽。因为多(10)个线程下载,服务器以为多(10)个客户端在下载,所以给每个客服端提供20k,合起来就200k。

02. 传统定时器技术回顾

java.util

Timer

一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。

void

schedule(TimerTask task, long delay)

安排在指定延迟后执行指定的任务。

void

schedule(TimerTask task, long delay, long period)

安排指定的任务从指定的延迟后开始进行重复的固定延迟执行

java.util

TimerTask  由 Timer安排为一次执行或重复执行的任务。实现了Runnable接口。

boolean

cancel()

取消此计时器任务。

abstract void

run()

此计时器任务要执行的操作。

定时器的应用:

//两个时间间隔发生,方式一:定义计数器

class MyTimerTaskextends TimerTask

{

@Override

publicvoid run()

{

count= (count+1)%2;

System.out.println("bombing!");

new Timer().schedule(new
MyTimerTask(), 2000+2000*count);

}

}

new Timer().schedule(new MyTimerTask(), 2000); //启动炸弹,2秒后

03. 传统线程互斥技术

线程的互斥与同步通信:

1、线程安全问题可以用银行转账来解释;两个业务同时拿到余额,一个存一个取。

2、使用synchronized代码块及其原理;  任意对象都可作为锁

3、使用synchronized方法;   this

4、分析静态方法所使用的同步监视器对象是什么?

方法所在类文件字节码

总结:如果多个线程执行的代码要求同步,需要加上synchronized;即一个线程没有将synchronized修饰的代码执行完时,其他线程不能进来执行。这里要注意,需要同步的代码(同一份)可以定义在不同的块中,但是为了同步,必须保证使用同一把锁。

线程的同步互斥的图文解说:

线程1        线程2         synchronized(obj)的作用主要是其中obj的作用

|               |

-------------------------------         a和b这两段代码被两个线程执行时要互斥;

a………       b……         要互斥的代码必须用synchronized代码块包围。

第二组代码要互斥,即c与d互斥,但a与c不互斥

线程3        线程4        cpu可同时进入a和c中进行替换

|               |

-------------------------------       用什么进行互斥分组?就是synchronized中的obj

c………         d……       值不同,而分为不同的互斥组。

04. 传统线程同步通信技术

5、wait与notify实现线程间的通信;

用面试宝典中的子线程循环10次和主线程循环5次,两者交替运行50的例子讲解;

为了观察程序的运行结果,可以在eclipse中设置运行对话框,让结果重定向到一个文本文件,然后用UE去查看该文件中的特殊的行号处是否正好对应了线程的切换点;

经验:要用到共同数据(包括同步锁)或同步算法的若干个方法应该归在同一个类身上,这种设计正好体现了高类聚和程序的健壮性。

package cn.itcast.heima;

/**

*
面试题:子线程循环10次,接着主线程循环10次;

*
接着又回到子线程循环10次,然后再让主线程循环10此。

*
交替循环50次

* @author Administrator

*

*/

publicclass TraditionalThreadCommunication

{

publicstaticvoid main(String[] args)

{

final Business business =new Business();    //为什么要加final

//子线程

new Thread(new Runnable(){

publicvoid run()

{

for(int i=1;i<=50;i++)

{

business.sub(i);       //每次循环,执行十次,然后等待唤醒main

}

}

}).start();

for(int i=1;i<=50;i++)

{

business.main(i);

}

}

//此处不加static,在main方法中new内部类对象会出错,

//是为什么?因为静态只能访问静态?

staticclass Business

{

privatebooleanbShouldSub =true;

publicsynchronizedvoid sub(int
i)

{

while(!bShouldSub)  //不该子线程运行了就让子线程等

{

try {

this.wait();

}
catch
(InterruptedException e) {

e.printStackTrace();

}

}

for(int j=1;j<=10;j++)

{

System.out.println("sub Thread sequece of"+j+",loop
of "+i);

}

bShouldSub=false;

this.notify();  //唤醒主线程

}

publicsynchronizedvoid main(int
i)

{

while(bShouldSub)

{

try {

this.wait();

}
catch
(InterruptedException e) {

e.printStackTrace();

}

}

for(int j=1;j<=20;j++)

{

System.out.println("main Thread "+j+",loop
of "+i);

}

bShouldSub=true;

this.notify();  //唤醒子线程

}

}

}

05. 线程范围内共享变量的概念与作用

publicclass ThreadScopeShareData

{

//存放线程和数据的映射关系

privatestatic Map<Thread, Integer>threadData
=new HashMap<Thread, Integer>();

publicstaticvoid main(String[] args)

{

for(int i=0;i<2;i++)  //产生两个线程

{

new Thread(new Runnable(){

publicvoid run()

{

int data =new Random().nextInt();

System.out.println(Thread.currentThread().getName()+" get data 
"+data);

threadData.put(Thread.currentThread(), data);

new A().get();

new B().get();

}

}).start();

}

}

06. ThreadLocal类及应用技巧

ThreadLocal实现线程范围的共享变量:

1、每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录,key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法,这样会更块释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。

2、ThreadLocal的应用场景:

订单处理包一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在一个事务中完成,通常也即同一个线程中进行处理,如果累加公司应收款的操作失败,则应该把前面的操作回滚,否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中;

银行转账包含一系列操作:把转出账户的余额减少,把转入账户的余额增加,这两个操作要在同一个事务中完成,它们必须使用相同的数据库对象,转入和转出操作的代码分别是两个不同的账户对象的方法。

例如Strut2的ActionContext,同一段代码被不同的线程调用运行的,该代码操作的数据是每个线程各自的状态和数据,对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说,不管调用getContext方法多少次和在那个模块中getContext方法,拿到的都是同一个。

实例:

publicclass ThreadLocalTest

{

//定义一个全局变量的ThreaadLocal变量,只能存放一个数据

privatestatic ThreadLocal<Integer>x
=new ThreadLocal<Integer>();

//定义一个全局变量的ThreaadLocal变量,存放一个对象,线程的所有数据封装在这个对象中

privatestatic ThreadLocal<MyThreadScopeData>myThreadScopeData
=

new ThreadLocal<MyThreadScopeData>();

publicstaticvoid main(String[] args)

{

for(int i=0;i<2;i++)  //产生两个线程

{

new Thread(new Runnable(){

publicvoid run()

{

int data =new Random().nextInt();

System.out.println(Thread.currentThread().getName()+" get data 
"+data);

x.set(data); //向ThreadLocal变量x中存储一个随机值

MyThreadScopeData.getThreadInstance().setName("name"+data);

MyThreadScopeData.getThreadInstance().setAge(data);

//调用其他类的方法,获取x的值,多个类在同一线程中获取到的值是相同的

new A().get();

new B().get();

}

}).start();

}

}

staticclass A

{

publicvoid get()

{

//读取这个ThreadLocal变量的值

int data =x.get();

System.out.println("A from "+Thread.currentThread().getName()+"
get data: "+data);

MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();

System.out.println("A from "+Thread.currentThread().getName()

+" getMyData: "+ myData.getName()+","+myData.getAge());

}

}

staticclass B

{

publicvoid get()

{

int data =x.get();

System.out.println("B from "+Thread.currentThread().getName()+"
get data: "+data);

MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();

System.out.println("B from "+Thread.currentThread().getName()

+" getMyData: "+ myData.getName()+","+myData.getAge());

}

}

//将线程的局部变量封装成对象,这样ThreadLocal中存放一个对象就等于存了几个数据

staticclass MyThreadScopeData

{

//单例设计模式

private MyThreadScopeData(){}

publicstatic/*synchronized*/ MyThreadScopeData
getThreadInstance()

{

MyThreadScopeData instance =map.get();

if(instance ==null)

{

instance =new MyThreadScopeData();

map.set(instance);

}

return instance;

}

//private static MyThreadScopeData instance = null;

privatestatic ThreadLocal<MyThreadScopeData>map
=

new ThreadLocal<MyThreadScopeData>();

private Stringname;

privateintage;

public String getName() {

returnname;

}

publicvoid setName(String name) {

this.name = name;

}

publicint getAge() {

returnage;

}

publicvoid setAge(int
age) {

this.age = age;

}

}

}

07. 多个线程之间共享数据的方式探讨

1、如果每个线程执行的代码相同,可使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。

2、如果每个线程执行的代码不同,这时就需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:

A、将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

B、将这些Runnable对象作为某个类中的内部类,共享数据作为这个外部类中的成员变量。每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥通信,作为内部类的各个Runnable对象调用外部类的这些方法。

C、上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。

总之,要同步互斥的几段代码最好是分别在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现他们之间的同步互斥和通信。

08. java5原子性操作类的应用

1、了解java.util.concurrent.atomic包;

看atomic包文档页下面的介绍,可以对基本数据AtomicInteger,对数组中的基本数据AtomicIntegerArray,对类中的基本数据AtomicIntegerFieldUpdater等进行操作。

09. java5线程并发库的应用

Java5中的线程并发库:

2、看java.util.concurrent包及子包的API帮助文档;

看concurrent包的帮助文档,对并发库中涉及的内容有一个总体上的介绍。

类Executors

此包中所定义的ExecutorExecutorServiceScheduledExecutorServiceThreadFactoryCallable类的工厂和实用方法

3、了解java.util.concurrent.lock包

线程池:

线程池的概念与Executors类的应用

A、理解:在线程池编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池拿到任务后,它就在内部招有无空闲的线程,再把任务交给内部某个空闲线程,这就是封装。记住,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池中提交多个任务。

B、创建固定大小的线程池:

ExecutorService threadPool = Executors.newFixedThreadPool(3);

创建缓存线程池:

ExecutorService threadPool = Executors.newCachedThreadPool();

创建单一线程池:(如何实现线程死掉后重新启动一个线程)

ExecutorService threadPool = Executors.newSingleThreadExecutor();

C、关闭线程池:

void

shutdown()

启动一次顺序关闭,执行以前提交的任务,但不接受新任务。

List<Runnable>

shutdownNow()

试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

D、用线程池启动定时器:

调用ScheduleExecutorService的schedule方法,返回的SchedulFuture对象可以取消任务。

支持间隔重复任务的定时方式,不直接支持绝对定时方式,需要转换成相对时间方式。

实例:

Executors.newScheduledThreadPool(3).scheduleAtFixedRate(

new Runnable(){@Override

publicvoid run() {

System.out.println(Thread.currentThread()+" bombing!");

//Thread[pool-2-thread-1,5,main]  bombing!

}},

1,    //10秒后炸

2,    //以后每隔2秒炸一次

TimeUnit.SECONDS);

10. Callable与Future的应用

java.util.concurrent

接口 Callable<V>

返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做call 的方法。Callable 接口类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是Runnable
不会返回结果,并且无法抛出经过检查的异常。

接口 Future<V>
表示异步计算的结果

//创建线程池

ExecutorService threadPool2 = Executors.newSingleThreadExecutor();   CompletionService<Integer> completionService =

new ExecutorCompletionService(threadPool2);

for(int i=1;i<=10;i++) //提交10个任务,任务会返回结果

{

finalint seq = i;

completionService.submit(new Callable<Integer>() {

public Integer call()throws
Exception {

Thread.sleep(new Random().nextInt(5000));//等待5秒以内随机

return seq;

}

});

}

//获取任务执行结果

for(int i=0;i<10;i++)

{

try {

//获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待。

System.out.println(completionService.take().get());

} catch (Exception e) {

e.printStackTrace();

}

}

11. java5的线程锁技术

java.util.concurrent.locks

接口 LockLock

实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

锁本身也应该是一个对象,两个线程执行的代码片段要实现同步互斥,他们必须使用用一个Lock对象。锁是上在代表要操作的资源的类的内部方法中,而不是线程代码中。

接口 Condition

ConditionObject 监视器方法(waitnotify
notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock
实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

12. java5读写锁技术的妙用

读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,就上写锁。总之,读的时候上读锁,写的时候上写锁。

一个面试题:写一个缓存类。

import java.util.*;

import java.util.concurrent.locks.*;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**

*
写一个缓存系统,

*
获取数据,当系统中有这个数据时,直接返回这个数据,、

*
当没有这个数据时,去数据库中找,找到后缓存起来,

*
下次获取这个数据时就直接返回。

* @author Administrator

*

*/

publicclass CacheDemo

{

private Map<String, Object>cache =new
HashMap<String, Object>();

publicstaticvoid main(String[] args)

{

}

private ReadWriteLockrw1 =new
ReentrantReadWriteLock();

public Object getData(String key)

{

rw1.readLock().lock();    //多个线程可同时读

Object value =
null
;

try{

value =
cache.get(key);

if(value ==null) //缓存中没有这个对象

{

rw1.readLock().unlock();

rw1.writeLock().lock();   //只有一个线程去从数据库拿

try{

//如果3个线程同时来读,到rw1.writeLock().lock();

//有两个线程等待了,当第一个线程已经从数据库中拿数据

//避免另外两个线程醒来时又去拿,还要判断一下

if(value ==null)

{

value ="aaaa";  //实际是去queryDB

}

}finally{

rw1.writeLock().unlock();

}

rw1.readLock().lock();

}

}finally{

rw1.readLock().unlock();

}

return value;

}

}

java--加强之 Java5的线程并发库的更多相关文章

  1. 使用Java线程并发库实现两个线程交替打印的线程题

    背景:是这样的今天在地铁上浏览了以下网页,看到网上一朋友问了一个多线程的问题.晚上闲着没事就决定把它实现出来. 题目: 1.开启两个线程,一个线程打印A-Z,两一个线程打印1-52的数据. 2.实现交 ...

  2. Java多线程与并发库高级应用-java5线程并发库

    java5 中的线程并发库 主要在java.util.concurrent包中 还有 java.util.concurrent.atomic子包和java.util.concurrent.lock子包 ...

  3. 线程高级应用-心得8-java5线程并发库中同步集合Collections工具类的应用及案例分析

    1.  HashSet与HashMap的联系与区别? 区别:前者是单列后者是双列,就是hashmap有键有值,hashset只有键: 联系:HashSet的底层就是HashMap,可以参考HashSe ...

  4. 线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

    1.Lock相关知识介绍 好比我同时种了几块地的麦子,然后就等待收割.收割时,则是哪块先熟了,先收割哪块. 下面举一个面试题的例子来引出Lock缓存读写锁的案例,一个load()和get()方法返回值 ...

  5. 线程高级应用-心得4-java5线程并发库介绍,及新技术案例分析

    1.  java5线程并发库新知识介绍 2.线程并发库案例分析 package com.itcast.family; import java.util.concurrent.ExecutorServi ...

  6. JAVA多线程提高六:java5线程并发库的应用_线程池

    前面我们对并发有了一定的认识,并且知道如何创建线程,创建线程主要依靠的是Thread 的类来完成的,那么有什么缺陷呢?如何解决? 一.对比new Threadnew Thread的弊端 a. 每次ne ...

  7. Java中的线程--并发库中的集合

    线程中的知识点基本都已经学完了,看看Java5并发库中提供的集合... 一.可堵塞队列 队列包含固定长度的队列和不固定长度的队列 ArrayBlockQueue中只有put()方法和take()方法才 ...

  8. Java多线程(五) —— 线程并发库之锁机制

    参考文献: http://www.blogjava.net/xylz/archive/2010/07/08/325587.html 一.Lock与ReentrantLock 前面的章节主要谈谈原子操作 ...

  9. Android多线程研究(7)——Java5中的线程并发库

    从这一篇开始我们将看看Java 5之后给我们添加的新的对线程操作的API,首先看看api文档: java.util.concurrent包含许多线程安全.测试良好.高性能的并发构建块,我们先看看ato ...

随机推荐

  1. API创建/更新员工联系电话

    DECLARE ln_phone_id PER_PHONES.PHONE_ID%TYPE; ln_object_version_number PER_PHONES.OBJECT_VERSION_NUM ...

  2. 01_MyBatis EHCache集成及所需jar包,ehcache.xml配置文件参数配置及mapper中的参数配置

     1 与mybatis集成时需要的jar ehcache-core-2.6.5.jar mybatis-ehcache-1.0.2.jar Mybatis.日志.EHCache所需要的jar包如下 ...

  3. iOS中 按钮和标题完美各种排列/完美教程 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 前言:最近常常用到按钮和相应标题的组合,当按钮设置图片加标题时,触发范围较小,不易触发,最重要的是还要调试偏移量, ...

  4. (一〇二)静态库(.a)的打包

    库是代码的集合,根据代码公开程度,分为开源库和闭源库. 其中闭源库主要包括静态库和动态库,是经过编译的二进制文件,看不到具体实现. 静态库的拓展名是.a或者.framework,动态库则是.dylib ...

  5. 学习笔记7-Android短信发送器

    新建一个Android项目sns. 在String.xml添加文字 <resources> <stringname="app_name">Sns发送短信&l ...

  6. linux命令指usermod(管理用户以及权限的命令)

    usermod命令:用来修改用户帐号的各项设定. 示例:usermod -a -G usergroupnewuser 或者usermod -aGusergroup newuser 语法:usermod ...

  7. iOS平台添加Google Admob -2/2(Unity3D开发之八)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=572 在上一篇文章中主要是编写了 ...

  8. UNIX环境高级编程——线程同步之互斥锁、读写锁和条件变量(小结)

    一.使用互斥锁 1.初始化互斥量 pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;//静态初始化互斥量 int pthread_mutex_init( ...

  9. Linux的mount命令简介

    在Linux系统中,如果要使用硬盘.光盘.软盘或MO盘等存储设备,必须先进行挂装(Mount).当存储设备挂装完成之后,就可以将其作为一个目录来进行访问了.挂装设备需要使用mount命令.执行这一命令 ...

  10. OJ题:成绩排序

    题目描述 查找和排序 题目:输入任意(用户,成绩)序列,可以获得成绩从高到低或从低到高的排列,相同成绩 都按先录入排列在前的规则处理. 例示: jack 70 peter 96 Tom 70 smit ...