-------------------任务执行-------------------
1.CountDownLatch(闭锁) countDown():计数器,当一个线程调用就减1 await() 只有countDown计算机为零以后,await调用的进程才能继续执行。
完成指定数量的线程以后,被await的线程才能继续执行
(好比旧社会家里没有吃的,小孩吃完粗粮以后,父母才把剩下的吃了)
小孩就是指定的多少个线程,父母就是被await方法调用的对象
//例子CountDownLatch指定的是10个线程
//contdl.await();只有指定的10个线程都执行完成以后,执行这个方法的线程才能继续
public static void testCountDownLatch(final CountDownLatch contdl){
for(int i=0;i<10;i++){
System.out.println("start"+i);
Thread t=new Thread(new Runnable(){
@Override
public void run() {
try{
System.out.println("thread start!");
}catch(Exception e){
}
finally{
contdl.countDown();
}
}
});
t.start();
System.out.println("end"+i);
}
}
2.FutureTask 例子:
public static void testFutureTask() throws InterruptedException, ExecutionException{
FutureTask<String> fuTask=new FutureTask<String>(new Callable<String>(){
@Override
public String call() throws Exception {
System.out.println("call run");
return "true";
}});
Thread thread=new Thread(fuTask);
thread.start();
String flag=fuTask.get();
System.out.println(flag);
}
执行完成后,会输出ture的字符串,如果在调用get方法之前还没完成线程的执行,可能会中断
4.semaphore信号量 用来控制并发过程中,可以执行的进程数量。(同一时间内只能有固定数量的可以执行,好比厕所的坑数量一样)
//Semaphore semaphore=new Semaphore(2);
public static void testSemaphore(final Semaphore semaphore){
for(int i=0;i<10;i++){
Thread t=new Thread(new Runnable(){
@Override
public void run() {
try{
semaphore.acquire();
System.out.println("thread start!");
Thread.currentThread().sleep(5000);
}catch(Exception e){
}
finally{
semaphore.release();
}
}
});
t.start();
}
}
同时定义了10个线程,但是能同时执行的只有2个。因为在传入的参数进指定了,只能有2个执行
5.CyclicBarrier栅栏 用来阻塞一些方法,只有满足CyclicBarrier的条件,才可以放行
CyclicBarrier cyclicBarrier=new CyclicBarrier(10,new Runnable(){
@Override
public void run() {
System.out.println("runnable");
}
});
Test.testCyclicBarrier(cyclicBarrier);
public static void testCyclicBarrier(final CyclicBarrier cyclicbarrier){
for(int i=0;i<10;i++){
Thread t=new Thread(new Runnable(){
@Override
public void run() {
try{
System.out.println("thread start1!");
Thread.currentThread().sleep(2000);
cyclicbarrier.await();
System.out.println("thread start2!");
}catch(Exception e){
e.printStackTrace();
}
}
});
t.start();
}
}
代码会输出thread start1!以后进入阻塞,只有当满足10个的时候,才会执行CyclicBarrier中的runnable线程,完了以后才会执行thread start2!
6.在正常使用中我们多用list(arrayList)和map(hashMap),在并发中为了保证线程安全用ConcurrentHashMap和CopyOnWriteList
7.Executor接口,实现Executor接口的接口ExecutorService和Executor的区别就是添加了生命周期的方法
8.线程池的使用
Executors.newFixedThreadPool(num);创建一个固定大小的线程池
Executors.SimpleThreadPool();单线程
Executors.newScheduledThreadPool();支持定时任务的线程池
Executors.newCachedThreadPool();缓存线程池,可以创建任意个线程池,用完自动回收
example:
public static void testExecutor() throws InterruptedException, ExecutionException{
ExecutorService executor=Executors.newSingleThreadExecutor();
Callable<String> task=new Callable<String>(){
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
System.out.println("running!");
return "ok";
}};
Future<String> future=executor.submit(task);
System.out.println(future.get());
executor.shutdown();
System.out.println(executor.isShutdown());
}
//out
pool-1-thread-1
running!
ok
true
//现实的获取代码段
try {
System.out.println(future.get(1,TimeUnit.SECONDS));//指定多少秒内获取到数据,如果在指定的时间内没有获取到数据会TimeoutException
} catch (TimeoutException e) {
//e.printStackTrace();
future.cancel(true);//如果抛出TimeoutException异常,就取消该任务。
}
编写多线程高性能的代码是如果任务执行的时间较长,可以考虑使用限时的等待方法,在指定的时间内如果没有执行完成,就直接取消。
合理的执行线程池的大小对于性能的提高也很有影响
包和策略:(当队列被填满以后,包和策略就可以派上用场了)
AbortPolicy:默认使用该测试,如果队列满了,会抛出RegectdExecutionExecption,我们可以捕获异常做一些处理
CallerRybsPolicy:调用者运行,当使用该策略以后,会将调用线程的执行回退给调用者(可能是主线程)去执行,只要主线程就不会很快速的提交到队列
,如果大并发厉害的话,这样也可能导致tcp队列和客户端整体性能下降(因为提交的越来越多),不过这样可以给线程池足够的时间去消费队列中的任务。
DiscardPloicy:抛弃任务,增加任务
DiscardOldestPolicy:会将队列中下一个任务抛弃掉,尝试将新任务提交到队列中去,如果是一个优先级高的队列会有问题的~
9.毒丸对象(一个标志)
用来控制线程(队列)的结束
例如在生产者消费者队列中,如果要停止该执行线程,只需要产生一个毒丸对象,放到到队列中,当消费者检测到存在毒丸对象的时候退出执行。
10.如果递归循环没有数据联系,在必要的情况下也可以通过并行来提交执行性能。
11.Lock和ReentrantLock,ReadWriteLock-ReenrantReadWriteLock(Lock的实现类)必要的时候可以使用lock.tryLock();用来获取锁
12.如果线程在休眠或者阻塞时持有一个锁,通常是一种不好的做法,影响其他线程对资源的访问,可能造成更大的性能下降
13.线程的6种状态:new runnable blocked waiting timewaiting terminated
14.线程中常用的方法:
join():当线程A创建并调用了线程B,线程B调用了join方法的时候,线程A会挂起,直到线程B执行完成以后才会继续执行。(串联)join(long min)
setDaemon(true):线程A调用A.setDaemon(true)就是将线程A设置为守护线程
setUncaughtExceptionHandler(new ExceptionDemo()):用来捕获非运行时异常
例子:
public class ExceptionDemo implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName());
System.out.println(e.getMessage());
}
public static void main(String[] args) {
Thread d=new Thread(new Current());
d.setUncaughtExceptionHandler(new ExceptionDemo());
d.start();
}
}
class Current implements Runnable{
@Override
public void run() {
System.out.println(Integer.parseInt("BANC"));
}
}
15ThreadLocal使用:属性线程私有化
public class ThreadLocalDemo implements Runnable{
private Date date2=new Date();
private ThreadLocal<Date> date=new ThreadLocal<Date>(){
@Override
protected Date initialValue() {
return new Date();
}
};
@Override
public void run() {
System.out.println("date2"+date2);
System.out.println(date.get());
}
public static void main(String[] args) {
ThreadLocalDemo td=new ThreadLocalDemo();
for(int i=0;i<4;i++){
new Thread(td).start();
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出:
date2Thu Mar 19 15:44:49 CST 2015
Thu Mar 19 15:44:49 CST 2015
date2Thu Mar 19 15:44:49 CST 2015
Thu Mar 19 15:44:51 CST 2015
date2Thu Mar 19 15:44:49 CST 2015
Thu Mar 19 15:44:53 CST 2015
date2Thu Mar 19 15:44:49 CST 2015
Thu Mar 19 15:44:55 CST 2015
//输出很明显
-------------性能和伸缩性---------------------
并发导致的性能影响因素:
1.上下文切换 应用程序,操作系统,JVM都需要使用同一个CPU,就需要协调数据,导致性能开销
2.内存同步 (影响较小现在JVM会对代码进行优化,对于单线程执行的代码会去掉同步锁,对于集中处理可能方法粒度加快操作等)
3.阻塞(存在锁竞争,导致操作系统上下文切换)
减少锁的竞争:(减少锁持有的时间,降低锁的请求频率,使用带有协调机制的独占锁)
1.缩小锁的范围(快进快出)对于那些和同步无关的代码,尽量放到同步代码块的外面,尤其一些i/o操作,计算逻辑大的
2.减小锁的粒度(锁分解) 假如我们有2个集合,都进行add操作,我们完全可以定义两把锁,各自add使用各自的锁(保证原子性的前提下)。
3.锁分段:就是定义一组锁,根据一定的逻辑计算,每个线程访问对应的锁
exmaple:
private final Object[] locks=new Object[16];
private final int n=16;
synchronized(lock[i%n]){...}
4.替代独占锁
使用并发容器,读写锁,不可变对象和原子变量等
5.不要使用对象池(虽然可以对对象进行重复使用,减少GC回收性能,但是在并发环境中,可能因为操作同一个对象导致阻塞)
java并发编程笔记
最新推荐文章于 2024-03-18 21:51:20 发布