------- android培训、java培训、期待与您交流! ----------
一、传统线程技术
传统是相对于JDK1.5而言的
传统线程技术与JDK1.5的线程并发库
线程就是程序的一条执行线索/线路。
创建线程的两种传统方式
1)创建Thread的子类,覆盖其中的run方法,运行这个子类的start方法即可开启线程
2)创建Thread时传递一个实现Runnable接口的对象实例
总结:查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。
多线程并不一定会提高程序的运行效率。
多线程下载:并不是自己电脑快了,而是抢到更多服务器资源。
3)当在Thread子类中覆盖了run方法并编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么线程在运行时执行的是子类所覆盖的run方法。
1).public staticvoid main(String[] args) {
Threadthread = new Thread(){
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1:" +Thread.currentThread().getName());
System.out.println("2:" +this.getName());
}
}
};
thread.start();
2).Threadthread2 = new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1:" +Thread.currentThread().getName());
}
}
});
thread2.start();
3).new Thread(
newRunnable(){
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("runnable :"+ Thread.currentThread().getName());
}
}
}
){
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread :" +Thread.currentThread().getName());
}
}
}.start();
}
二、传统定时器
传统定时器的创建:直接使用定时器类Timer
a、过多长时间后炸
newTimer().schedule(TimerTask定时任务, Date time定的时间);
b、过多长时间后炸,以后每隔多少时间再炸
newTimer().schedule(TimerTask定时任务, Long延迟(第一次执行)时间, Long间隔时间);
TimerTask与Runnable类似,有一个run方法
Timer是定时器对象,到时间后会触发炸弹(TimerTask)对象
注意:内部类中不能声明静态变量
定义一个静态变量private static count = 0;
在run方法内部:count=(count+1)%2;
将定时器的时间设置为:2000+2000*count
示例:
private static int count = 0;
public static voidmain(String[] args) {
class MyTimerTask extends TimerTask{
@Override
public void run(){
count = (count+1)%2;
System.out.println("bombing!");
new Timer().schedule(newMyTimerTask(),2000+2000*count);
}
}
new Timer().schedule(new MyTimerTask(), 2000);
while(true){
System.out.println(newDate().getSeconds());
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
定时器还可以设置具体时间。
三、传统线程互斥技术
需要注意的地方:
内部类不能访问局部变量,要访问需加final。
静态方法中不能创建内部类的实例对象。
互斥方法:
同步代码块:synchronized (lock){}
同步方法:方法返回值前加synchronized,同步方法上边用的锁就是this对象,静 态同步方法使用的锁是该方法所在的class文件对象
使用synchronized关键字实现互斥,要保证同步的地方使用的是同一个锁对象。
四、传统线程同步通信技术
小总结:要用到共同数据(包括同步锁)或相同算法的多个方法要封装在一个类中,锁是上在代表要操作的资源类的内部方法中的,而不是上在线程代码中的。这样写出来的类就是天然同步的,只要使用的是同一个new出来的对象,那么这个对象就具有同步互斥特性。
判断唤醒等待标记时使用while增加程序健壮性,防止伪唤醒。
五、ThreadLocal实现线程范围的共享变量
JDK1.5提供了ThreadLocal类来方便实现线程范围内的数据共享,它的作用就相当于上一节中的Map。
importjava.util.Map;
importjava.util.Random;
public classThreadLocalTest {
private static ThreadLocal<Integer>x = new ThreadLocal<Integer>();
private staticThreadLocal<MyThreadScopeData> myThreadScopeData =
new ThreadLocal<MyThreadScopeData>();
public static void main(String[] args) {
for(inti=0;i<2;i++){
new Thread(new Runnable(){
@Override
publicvoid run() {
intdata = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + " has put data :" + data);
x.set(data);
MyThreadScopeData.getThreadInstance().setName("name"+ data);
MyThreadScopeData.getThreadInstance().setAge(data);
new A().get();
new B().get();
}
}).start();
}
}
static class A{
publicvoid get(){
int data = x.get();
System.out.println("Afrom " + Thread.currentThread().getName()+ " get data :" + data);
MyThreadScopeData myData =MyThreadScopeData.getThreadInstance();
System.out.println("A from " +Thread.currentThread().getName()
+ " getMyData: " +myData.getName() + "," +
myData.getAge());
}
}
六、java原子性操作类的应用
Java5的线程并发库
java.util.concurrent在并发编程中很常用的实用工具类。
|----locks为锁和等待条件提供一个框架的接口和类,
它不同于内置同步和监视器
|----atomic类的小工具包,支持在单个变量上解除锁的线程安全编程。
可以对基本类型、数组中的基本类型、类中的基本类型等进行操作
|----AtomicInteger
七、java线程并发库的应用
1.线程池:先创建多个线程放在线程池中,当有任务需要执行时,从线程池中找一个空闲线程执行任务,任务完成后,并不销毁线程,而是返回线程池,等待新的任务安排。
2.线程池编程中,任务是提交给整个线程池的,并不是提交给某个具体的线程,而是由线程池从中挑选一个空闲线程来运行任务。一个线程同时只能执行一个任务,可以同时向一个线程池提交多个任务。
3.线程池创建方法:
(1)创建一个拥有固定线程数的线程池。
ExecutorService threadPool =Executors.newFixedThreadPool(3);
(2)创建一个缓存线程池。
ExecutorService threadPool =Executors.newCacheThreadPool();
(3)创建一个只有一个线程的线程池。
ExecutorService threadPool =Executors.newSingleThreadExector();
(4)往线程池中添加任务。
threadPool.executor(Runnable)
(5)关闭线程池。
threadPool.shutdown() 线程全部空闲,没有任务就关闭线程池
threadPool.shutdownNow() 不管任务有没有做完,都关掉
八、 Callable与Future的应用:
1)Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
2)Callable要采用ExecutorService的submit方法提交,返回的future对象可以取消任务。
3)CompletionService用于提交一组的Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
示例如下:
publicclass CallableAndFuture {
public static void main(String[] args) {
ExecutorServicethreadPool = Executors.newSingleThreadExecutor();
Future<String>future =
threadPool.submit(
newCallable<String>() {
public String call() throws Exception {
Thread.sleep(2000);
return"hello";
};
}
);
System.out.println("等待结果");
try{
System.out.println("拿到结果:" + future.get());
}catch (InterruptedException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
九、java5的线程锁技术
总结:1.Lock比传统线程模型中的synchronized更加面向对象,锁本身也是一个对象,两个线程执行的代码要实现同步互斥效果,就要使用同一个锁对象。锁要上在要操作的资源类的内部方法中,而不是线程代码中。
2.锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally或 try-catch 加以保护,以确保在必要时释放锁。
十、Condition的应用
1.Condition的功能类似在传统线程技术中的Object.wait()和Object.natify()的功能,传统线程技术实现的互斥只能一个线程单独干,不能说这个线程干完了通知另一个线程来干,Condition就是解决这个问题的,实现线程间的通信。
2.Condition实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition实例,请使用其 newCondition()方法。
十一、Lock和Condition的应用
static class Business {
Locklock = new ReentrantLock();
Conditioncondition1 = lock.newCondition();
Conditioncondition2 = lock.newCondition();
Conditioncondition3 = lock.newCondition();
private int shouldSub = 1;
public void sub2(int i){
lock.lock();
try{
while(shouldSub != 2){
try {
condition2.await();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(intj=1;j<=10;j++){
System.out.println("sub2 threadsequence of " + j + ",loop of " + i);
}
shouldSub = 3;
condition3.signal();
}finally{
lock.unlock();
}
}
public void sub3(int i){
lock.lock();
try{
while(shouldSub != 3){
try {
condition3.await();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(intj=1;j<=20;j++){
System.out.println("sub3 threadsequence of " + j + ",loop of " + i);
}
shouldSub = 1;
condition1.signal();
}finally{
lock.unlock();
}
}
public void main(int i){
lock.lock();
try{
while(shouldSub != 1){
try{
condition1.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for(intj=1;j<=100;j++){
System.out.println("main threadsequence of " + j + ",loop of " + i);
}
shouldSub= 2;
condition2.signal();
}finally{
lock.unlock();
}
}
十二、Semphore:实现信号灯,通常用于限制可以访问的某些资源的线程数目
Semaphore可以维护当前访问自身的线程个数,并且提供了同步机制。
Semaphore通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。
Semaphore只对可用许可的号码进行计数,并采取相应的行动。
Semphoresp = new Semphore(6);
sp.availablePermits();
sp.acquire();//请求
sp.release();//释放,添加一个许可,从而可能释放一个正在阻塞的获取者
总结:传统互斥只能内部释放锁this.unlock(),进去this.lock()晕倒了别人就没法进去了;用信号灯可以外部释放,其他线程可以释放再获取sp.release() sp.acquire()。
十三、CyclicBarrier
集合等待,他允许一组线程互相等待,直到到达某个公共屏障点。
CyclicBarrier cb = newCyclicBarrier(3);
cb.await();//到了其他人没来就等
cb.getNumberWaiting();//当前等待数量
总结:CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。
十四、CountDownLatch同步工具
倒计时计数器,调用CountDownLatch对象的countDown方法,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,他允许一个或多个线程等待。
十五、Exchanger:数据交换
用于实现两个数据之间的交换。
exchanger.eachange(data)//将自己的数据交换出去
exchange():等待另一个线程到达此交换点,然后将给定的线程传送给该线程,并接受该线程的对象。
十六、ArrayBlockingQueue:可阻塞的队列
BlockingQueue queue = newArrayBlockingQueue(3);
queue.put(1);//如果放满就会堵塞
queue.take();//移除元素
BlockingQueue方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,第二种是返回一个特殊值(null或 false,具体取决于操作),第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。下表中总结了这些方法:
| 抛出异常 | 特殊值 | 阻塞 | 超时 |
插入 | ||||
移除 | ||||
检查 | 不可用 | 不可用 |
总结:
BlockingQueue不接受 null元素。
BlockingQueue实现主要用于生产者-使用者队列,但它另外还支持Collection接口。
BlockingQueue实现是线程安全的。
------- android培训、java培训、期待与您交流! ----------