-------android培训、java培训、期待与您交流!----------
内容概述:
1、传统计时器(TraditionalTimer):
Timer.schedule(TimerTask task, long delay, long period)等;
2、ThreadPool(线程池/并发库)
ExecutorService(1、指定数量线程的线程池;2、延迟或定期执行任务的线程池)
3、Synchronized (wait、notify 和 notifyAll)
4、Lock和Condition (lock()与unlock(),await与signal、signalAll)
5、Semaphonre(信号灯,计数信号量,.acquire()和.release()来控制和释放进入同步区域的许可)
6、CyclicBarrier(同步执行线程数,在屏障点等待,直到线程全部集齐继续执行)
7、CountDownLatch(计数锁存器,执行线程任务中countDown()递减count值,当数值归零时(已完成进入等待)线程恢复启动)
8、Exchanger(对元素进行配对和交换的线程同步点)
9、ReadWriterLock(读写锁,互斥,当读取数据为null时,进入写锁区域写入数据)
10、BlockingQueue (类似IO管道流,一线程存储数据,另一线程释放数据)
【1、ArrayBlockingQueue(数组阻塞队列,固定大小、有界队列)、
2、LinkedBlockingQueue(链表阻塞单端队列(先进先出))、
3、LinkedBlockingDeque(链表阻塞双端队列)】
11、SynchronousQueue 同步队列(向队列中存储数据,当前一个数据未被取出时阻塞);
12、同步集合的旧方法: Collections.synchronizedXXX(XXX xxx);
13、线程同步集合ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList、CopyOnWriteArraySet.
———————————————多线程与定时器技术——————————————
一、基础回顾:
1、多线程创建的两种方式: extends Thread 与implements Runnable;
代码示例:
extends Thread:
implements Runnable:
2、两者区别:
3、理解下面的特殊形式:
new Thread(new Runnable(){run() }){run() }.start();
start()线程执行的run()方法:
优先执行Thread中的run()方法,除非没有才会执行Runnable中的run()方法。
———————————————传统计时器技术——————————————
二、传统计时器(定时器/监视器):
(一)Timer:计时器【java.util包】 ;
.schedule(TimerTask task,Date time): 安排在指定的时间时执行指定的任务;
.schedule(TimerTask task,Date firstTime,long p)
.schedule(TimerTask task, long delay, long period)
PS:TimerTask: 计时器任务,创建或继承此类并复写该类中的run()方法;
(二)[高级提升]计时器的循环嵌套:
1、创建一个计时器,计时器任务内部封装方法并重复调用该方法,可实现间隔性执行(可用固定延迟时间重复执行的方法来简单实现);
2、创建两个计时器任务,A任务中调用B任务,B中调用A,则可实现交叉运行两个任务;
3、扩展: 通过设定数值变量,实现不同时间间隔的调用本方法);
备注:
A、计时器对象属于内部类,因此计时器任务调用局部变量需被final修饰。
B、内部类不能创建静态变量,因此要将全局使用的变量定义为静态成员变量。
—————————
PS:
Date(日期):java.util.Date日期类;
相关抽象类: Calendar(日历类)与DateFormat(格式化日期类)。
Data: 数据/资料;
—————————
(三)Synchronized: 同步
1、同步代码块: 类似static代码块使用方式,被大括号包含的代码为相同锁对象时,多线程同步执行代码;【锁: 可以是任意对象。】
2、同步函数: 被Synchronized修饰的方法/函数,方法中的代码为同步代码;
【锁为this: 即调用该方法的类,在同步区域中通过wait()与notify()来控制线程操作】
新的多线程同步锁机制:
经验(重要):要用到共同数据(包括同步锁)或共同算法的若干方法应该归到同一个类上,这种设计正好体现了高类聚和程序的健壮性。
其他:
解析: 1、静态类/方法随类加载而加载,无需创建类实例对象;
2、内部类可以直接访问它外部类的成员变量,因此访问非静态内部类需通过外部类对象,也就需要实例化外部类对象;
3、静态内部类则无需外部类对象,也不需要创建内部类的实例对象。
B、内部类分为:
内部类注意事项:
1、访问成员内部类的唯一途径就是通过外部类的对象;
2、静态嵌套类可以像其他静态成员一样,没有外部类对象时,也能够被访问,但它不能(直接)访问外部类的成员和方法;
3、方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
4、建立匿名内部类是重写父类的方法,而不是创建新的方法。
—————————线程范围内的共享变量—————————
三、线程范围内的共享变量:
1、ThreadLocal:创建一个线程本地变量;
通常是private static修饰ThreadLocal实例对象。
方法:
set(T value):将此线程局部变量的当前线程副本中的值设置为指定值;
get()
remove()
2、代码示例:
private static ThreadLocal tl = newThreadLocal();
//………………………………
new Thread(new Runnable(){
public void run() {
int i = (int) (Math.random()*10+1);
tl.set(i);
System.out.println(Thread.currentThread().getName()+":::"+i);
new A().getA();
//new B().getB(); //重复A类getA方法,只是重复验证。
}
}).start();
同类中多线程执行静态内部类方法:
static class A{
public void getA(){
System.out.println("A:"+Thread.currentThread().getName()+"::" +tl.get());
}
}
结果: 如果多线程重复执行run()方法,每个线程输入值与getA()得到的值相同,而不同线程之间是不同的。
【类似创建: Map,Tvalue> : 将每个线程及其线程局部变量存储到Map集合中,根据线程名获取与该线程对应的变量值。】
3、应用:
(该变量可以是任意对象类型T: String、基本数据类型对象或者数据类。)
B、应用于单例设计模式(饿汉式): 封装ThreadLocal在类中,不同线程来获取同一个类变量对象,而取走不同的实例对象。
注意:
B、在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
疑惑:
线程什么时候终结,是否有方法可以获得线程终结的反馈,以便清除ThreadLocal等中的内容。
(类似要求虚拟机结束时删除某个方法程序)
【当多个线程操作共享对象和数据时,可以将操作的对象或数据以及操作的方法封装成单独的个体(类中的成员或方法),而操作的多个线程(封装成类或匿名内部类,类的内容)就是通过调用该类方法来实现操作该对象或数据的动作。】
————————————
其他:
void addShutdownHook(Thread hook): 添加某个线程,当虚拟机终止时启动执行;
Interface
addThreadFilter(ThreadReferencethread)
————————————Java线程池/并发库——————————————
四、ThreadPool: 线程池/并发库:
(1)创建线程池方式:
ExecutorService
例: newSingleThreadExecutor()
等(更多详见
java.util.concurrent.Executors类)
解释:
一个线程当完成某个任务后,会去寻找其他未执行的任务,直到全部完成。
(2)线程池类型:
1、固定线程数量的线程池(完成任务后自动寻找未执行任务,直到全部完成);
2、缓存线程数量的线程池(根据需要执行的任务数量启动对应数量的线程);
3、单一线程池(当该线程死亡时会自动创建新的线程来继续执行任务);
newScheduledThreadPool().scheduleAtFixedRate(...)
:定时器线程池【指定延迟时间后执行、时间间隔重复执行】;
【因为没有像定时器般提供指定日期方法,可通过指定时间减去当前时间获得时间间隔】
ExecutorSevice方法:
.execute(Runnable command)
.shutdown
.shutdownNow
Future
【该结果可通过Future对象get()方法获得,是等待执行结束才能获得,如果任务中有延迟也获取结果步骤阻塞,直到获得结果??????】
Future
CompletionService : 存储全部返回结果;
CopyOnWriteArrayList : 特殊集合(允许迭代同时向集合中添加数据)
队列
BlockingQueue
SynchronousQueue
————————————读写锁————————————————
五、读写锁:
.readLock()
.writeLock()
.lock()和.unlock()加锁与释放
使用:
共享数据,将只能有一个线程能写该数据,但可以有多个线程同时读取该数据。
备注:
1、读写锁的特殊形式:
更新锁(既能读又能写的锁,只能自身添加,即拥有写锁的线程在写入数据过程中启动读锁,实现读写操作并存,这也就将写锁降级为更新锁)
2、缓冲代理(实体对象代理):
缓冲系统伪代码:
其他:
ReadWriteLock:读写锁(多线程操作共同数据,多线程进入读锁区域,当读取数据为null时,进入写锁区域写入数据)
Lock和Condition:
———————————————Semaphore信号灯—————————————
六、Semaphore: 信号灯(计数信号量)
使用:
方法:
release();
acquire();
availablePermits();
————————————————同步辅助类——————————————
七、同步辅助类:
CyclicBarrier:
构造方法:
方法:
.await();: 线程进入等待,当同步执行线程都进入等待则全部被唤醒,一同执行后续操作。
CountDownLatch : 计数锁存器;
构造方法: CountDownLatch
(int count);
指定计数值。
定义:
countDown(): 递减锁存器的计数数值
await()
await(long timeout,TimeUnitunit)
———————————————Exchanger————————————————
八、Exchanger
方法:
V
【两个交换数据线程进入exchange()方法,会先处于等待直到另一个线程进入,交换彼此数据后,继续进行各自线程后续操作】
——————————————BlockingQueue(队列)—————————————
九、阻塞队列(BlockingQueue):
1、不同功能实例子类:
ArrayBlockingQueue
LinkedBlockingQueue :链表阻塞队列(先进先出、单端队列)
LinkedBlockingDeque : (链表阻塞双端队列)
2、使用: 获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。
【简单概括: 多线程执行,一个线程等待读取数据通过add()或put()数据到队列(没有时处于阻塞),另一个线程提取/获取数据,根据获得的数据操作其他行为。】
PS:
【详见java.util.concurrent包中接口BlockingQueue】
3、特殊应用:
因为当BlockingQueue阻塞队列中存满时会阻塞,如果队列元素被取则恢复并继续向下执行,因此通过给两个具有1个空间的队列,存储、交叉取出,就不需要通过同步也能实现同步通知功能。
(向A方法中阻塞队列存储元素,假如队列为满,A方法阻塞;但B方法中执行到位A方法中的阻塞队列取出元素时,A方法恢复正常继续向下执行,两者交替执行,实现同步通知)
4、BlockingQueue与Semaphore的不同:
BlockingQueue是一方存放数据,另一方释放数据;
Semaphore通常则是由同一方设置和释放信号量。
5、匿名构造方法(函数):{
【在其他构造函数运行之前运行,创建几个对象就会运行几次】
【不同于静态代码块、..........等】
————————————线程同步集合——————————————
十、线程同步集合:
因为一般Java集合的隐患:
所以提供支持多线程同步操作的集合等。
1、Collections.SynchronizedMap()等
2、线程并发库:
ConcurrentHashMap
ConcurrentSkipListMap
ConcurrentSkipListSet : 同上,类似TreeSet(但具备线程同步)
CopyOnWriteArrayList
CopyOnWriteArraySet
十一、Java集合与遍历时异常隐患:
1、next()方法在取出元素的同时,会将迭代角标后移一位;
【因此一次hasNext()的判断,只允许运行一次next(),如果要对迭代元素进行多次操作,应该赋值给创建的变量,对赋值变量进行多次操作;
例如:
2、hasNext()方法:return cursor != size();[迭代角标位是否等于元素个数];
因此在遍历集合时进行增删,会直接修改集合中元素个数;造成结果:
Java解决方案:
总结:
注意(重要小细节):
public String getKey(Stringkey1,String key2){
类似:
sop(s1==s2);
sop(s1.equals(s2));
结论:
因此当需要比较不同时出现的两个字符串对象是否相同时可通过集合容器的equals方法;
例如: 多线程互斥synchronized(key){...}时,可将key判断是否在集合中,没有则存入,有则提取集合中对应key值赋值转换,实现对象地址值统一。