多线程学习
Posted vvning
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程学习相关的知识,希望对你有一定的参考价值。
1.什么是多线程?主要理解进程和线程。
进程:操作系统管理的基本运行单元,只是一个静态的概念,本身不能被执行。一个.exe文件,一个class文件就是一个进程,平时我们所说的一个进程开始执行了,其实是主线程被执行了。
多线程:就是一个进程里不同的执行路径。其实CPU一次只能执行同一个线程,由于不同线程之间切换的时间差很小,给人的感觉是同时进行而已。(个人理解为将CPU的利用率最大化)
2.如何创建线程?
实现runnable接口
继承Thread类(Thread类其实也是实现了runnable接口的子类)。
常见的创建线程的方式:
Thread thread = new Thread(
public void run(){
//实现的业务逻辑
}
).start();
3.线程的生命周期:
创建一个线程-》runnable状态等待被调用-》running状态-》dead(执行完或者抛出异常)
阻塞状态在runnable 和 running 之间
注意:如果直接调用线程的run方法即thread.run();那么是方法调用,不会呈多线程效果。
4.线程控制的基本方法
View Code
View Code
1.isAlive()判断线程是否终止
2.getPriority()/setPriority()获取/设置优先级
3.Thread.sleep()当前线程睡眠,可指定毫秒数
4.join()等待线程结束,在恢复此线程继续执行
5.yield() 让出CPU,进入runnable等待被调度
6.wait()当前线程进入对象的waitpool
7.notify()/notifyAll()唤醒一个或者唤醒对象所有的waitPoll
5.sleep()方法和wait()方法的区别 ?
1.sleep方法是Thread类的,而wait方法是Object类的
2.调用sleep方法不会释放对象锁,调用wait方法会释放对象锁
6.不要通过thread.stop()的方式终止线程,此方法为强制关闭线程,有些资源不会被释放。
7.关键字synchronized
同步代码块synchronized(this){
......
}
synchronized method(){.......} 二者都是在执行的过程中都是锁定当前对象,是多线程学习的重点。
此处有个细节:同步方法时虽然锁住的是对象,但是没有加锁的方法依然可以异步访问。
8.线程死锁:程序中过多的同步会产生死锁。代码演示:
![技术分享](/img/jia.gif)
1 /** 2 * 代码演示线程死锁 一个简单的死锁类 3 * 4 * 当DeadLock类的对象flag==1时(lock1),先锁定o1,睡眠500毫秒 5 * 6 * 而lock1在睡眠的时候另一个flag==0的对象(lock2)线程启动,先锁定o2,睡眠500毫秒 7 * 8 * lock1睡眠结束后需要锁定o2才能继续执行,而此时o2已被lock2锁定; 9 * 10 * lock2睡眠结束后需要锁定o1才能继续执行,而此时o1已被lock1锁定; 11 * 12 * lock1、lock2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。 13 */ 14 15 public class TestDeadLock implements Runnable { 16 public int flag = 1; 17 static Object o1 = new Object(), o2 = new Object(); 18 19 @Override 20 public void run() { 21 System.out.println("flag=" + flag); 22 if (flag == 1) { 23 synchronized (o1) { 24 try { 25 Thread.sleep(1000); 26 } catch (InterruptedException e) { 27 // TODO Auto-generated catch block 28 e.printStackTrace(); 29 } 30 synchronized (o2) { 31 System.out.println("1"); 32 } 33 } 34 } 35 if (flag == 0) { 36 synchronized (o2) { 37 try { 38 Thread.sleep(500); 39 } catch (InterruptedException e) { 40 // TODO Auto-generated catch block 41 e.printStackTrace(); 42 } 43 synchronized (o1) { 44 System.out.println("0"); 45 } 46 } 47 } 48 } 49 50 public static void main(String[] args) throws InterruptedException { 51 TestDeadLock lock1 = new TestDeadLock(); 52 TestDeadLock lock2 = new TestDeadLock(); 53 54 lock1.flag = 1; 55 lock2.flag = 0; 56 57 new Thread(lock1).start(); 58 new Thread(lock2).start(); 59 } 60 61 }
9.与多线程相关的几个API
threadLocal :是每个线程独立拥有的对象,在当前线程中数据共享。set() 、get()方法====Map<Thread,变量>
timer: 定时器 请见timertest。new Timer().schedule(new TimerTask(){});创建一个定时器->计划什么时候执行->创建一个任务放到计划里
CountDownLatch:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。await()->countDown()->当计数器为0时,程序才继续向下执行(很有用。)
![技术分享](/img/jia.gif)
1 package tcl.o2o.it.threadinjdk5; 2 3 import java.util.concurrent.CountDownLatch; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 7 public class Learn03CountdownLatchTest { 8 9 public static void main(String[] args) { 10 ExecutorService service = Executors.newCachedThreadPool(); // 创建一个线程池 11 final CountDownLatch cdOrder = new CountDownLatch(1);// 指挥官的命令,设置为1,指挥官一下达命令,则cutDown,变为0,战士们执行任务 12 final CountDownLatch cdAnswer = new CountDownLatch(3);// 因为有三个战士,所以初始值为3,每一个战士执行任务完毕则cutDown一次,当三个都执行完毕,变为0,则指挥官停止等待。 13 for (int i = 0; i < 3; i++) { 14 Runnable runnable = new Runnable() { 15 public void run() { 16 try { 17 System.out.println("线程" + Thread.currentThread().getName() + "正准备接受命令"); 18 cdOrder.await(); // 战士们都处于等待命令状态 19 System.out.println("线程" + Thread.currentThread().getName() + "已接受命令"); 20 Thread.sleep((long) (Math.random() * 10000)); 21 System.out.println("线程" + Thread.currentThread().getName() + "回应命令处理结果"); 22 cdAnswer.countDown(); // 任务执行完毕,返回给指挥官,cdAnswer减1。 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 } 27 }; 28 service.execute(runnable);// 为线程池添加任务 29 } 30 try { 31 Thread.sleep((long) (Math.random() * 10000)); 32 33 System.out.println("线程" + Thread.currentThread().getName() + "即将发布命令"); 34 cdOrder.countDown(); // 发送命令,cdOrder减1,处于等待的战士们停止等待转去执行任务。 35 System.out.println("线程" + Thread.currentThread().getName() + "已发送命令,正在等待结果"); 36 cdAnswer.await(); // 命令发送后指挥官处于等待状态,一旦cdAnswer为0时停止等待继续往下执行 37 System.out.println("线程" + Thread.currentThread().getName() + "已收到所有响应结果"); 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 service.shutdown(); // 任务结束,停止线程池的所有线程 42 43 } 44 }
10.Lock 和 Condition
jdk1.5新特性锁:
ReentrantLock通用锁和ReentrantReadWriteLock读写锁。
ReentrantLock和synchronized方法类似 只不过是通过lock()和unlock()控制边界。
重点读写锁:除了read和read之外的任和锁都互斥
11.condition:Condition的功能类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。
12.线程池的使用
由于线程的生命周期中包括创建、就绪、运行、阻塞、销毁阶段,当我们待处理的任务数目较小时,我们可以自己创建几个线程来处理相应的任务,但当有大量的任务时,由于创建、销毁线程需要很大的开销,运用线程池这些问题就大大的缓解了。
Executors.newFixedThreadPool(3)// 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
Executors.newCachedThreadPool()//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
Executors.newSingleThreadExecutor()//创建一个单线程化的线程池,它只会用唯一的工作线程来执行任、务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
corePoolSize、maximumPoolSize、largestPoolSize
13.jdk1.5新特性Callable和Future(Future用于接收实现了Callable接口的线程执行结果)
View Code
View Code
定义:callable接口类似于runnable,但是runnable不会有返回结果,callable有返回结果可以被future拿到
ExecutorService 的submit 和 excute的区别 ,前者有返回值future,execute没有返回值
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(es); 批量提交任务,并获取返回结果
![技术分享](/img/jia.gif)
1 package tcl.o2o.it.threadinjdk5; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 import java.util.concurrent.Future; 8 9 public class Learn06ThreadFutureAndCallable01 { 10 11 public static void main(String[] args) throws InterruptedException, ExecutionException { 12 // TODO Auto-generated method stub 13 ExecutorService es = Executors.newSingleThreadExecutor(); 14 Future<String> future = es.submit(new Callable<String>() { 15 @Override 16 public String call() throws Exception { 17 // TODO Auto-generated method stub 18 System.out.println("线程开始执行"); 19 Thread.sleep(1000); 20 return "hello world"; 21 } 22 }); 23 24 es.shutdown(); 25 System.out.println("等待返回结果"); 26 System.out.println(future.get()); 27 } 28 29 }
14.Semaphore-信号灯机制 当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制)。acquire和release方法。
![技术分享](/img/jia.gif)
1 public class Learn08SemaphoreTest { 2 public static void main(String[] args) { 3 ExecutorService service = Executors.newCachedThreadPool(); 4 final Semaphore sp = new Semaphore(3); 5 for (int i = 0; i < 5; i++) { 6 Runnable runnable = new Runnable() { 7 public void run() { 8 try { 9 sp.acquire(); 10 } catch (InterruptedException e1) { 11 e1.printStackTrace(); 12 } 13 System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" 14 + (3 - sp.availablePermits()) + "个并发"); 15 try { 16 Thread.sleep((long) (Math.random() * 10000)); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 System.out.println("线程" + Thread.currentThread().getName() + "即将离开"); 21 sp.release(); 22 // 下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元 23 System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" 24 + (3 - sp.availablePermits()) + "个并发"); 25 } 26 }; 27 service.execute(runnable); 28 } 29 } 30 }
15.fork/join框架:ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成,ForkJoinTask数组负责存放程序提交给ForkJoinPool的任务,而ForkJoinWorkerThread数组负责执行这些任务。
View Code
RecursiveAction:用于没有返回结果的任务。
RecursiveTask :用于有返回结果的任务。
![技术分享](/img/jia.gif)
1 package tcl.o2o.it.threadinjdk5; 2 3 import java.util.concurrent.ExecutionException; 4 import java.util.concurrent.ForkJoinPool; 5 import java.util.concurrent.Future; 6 import java.util.concurrent.RecursiveTask; 7 8 public class Learn09ForkJoinTest extends RecursiveTask { 9 private static int ThreadShold = 2; 10 int start; 11 int end; 12 13 private Learn09ForkJoinTest(int start, int end) { 14 this.start = start; 15 this.end = end; 16 } 17 18 @Override 19 protected Integer compute() { 20 int sum = 0; 21 boolean flag = (end - start) <= ThreadShold; 22 if (flag) { 23 for (int i = start; i <= end; i++) { 24 sum += i; 25 } 26 } else { 27 int middle = (start + end) / 2; 28 Learn09ForkJoinTest leftTask = new Learn09ForkJoinTest(start, middle); 29 Learn09ForkJoinTest rightTask = new Learn09ForkJoinTest(middle+1, end); 30 31 leftTask.fork();//把任务放到该任务的顶部 32 rightTask.fork(); 33 34 int leftResult = (Integer) leftTask.join();//执行完再往下走 35 int rightResult = (Integer) rightTask.join(); 36 37 sum = leftResult + rightResult; 38 } 39 return sum; 40 } 41 public static void main(String[] args) { 42 ForkJoinPool forkJoinPool = new ForkJoinPool(); 43 long beginTime = System.currentTimeMillis(); 44 Learn09ForkJoinTest task = new Learn09ForkJoinTest(1, 100000); 45 Future<Integer> result = forkJoinPool.submit(task); 46 try { 47 System.out.println(result.get()); 48 System.out.println( System.currentTimeMillis() - beginTime); 49 } catch (InterruptedException e) { 50 // TODO Auto-generated catch block 51 e.printStackTrace(); 52 } catch (ExecutionException e) { 53 // TODO Auto-generated catch block 54 e.printStackTrace(); 55 } 56 57 } 58 }
以上是关于多线程学习的主要内容,如果未能解决你的问题,请参考以下文章