博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
再次梳理多线程
阅读量:6424 次
发布时间:2019-06-23

本文共 2816 字,大约阅读时间需要 9 分钟。

     说起多线程,java开发人员都会觉得这是必须的知识,但是在我们(绝大多数的应用开发)平时的时候,写的多线程代码并不多。就比如我而言,在生产环境下写的多线程也就是一个批量上传百万条数据时候开了5个线程进行批量上传,带来的提高也就提高了3点多倍的速度。所以很多开发人员在提到多线程的时候就会感觉很熟悉,但是却讲不清楚。

     那为什么需要多线程?因为现在是多cpu的时代。多个cpu一起干活(一起干一件事情还是多件事情)。其实并发提高的效率是指单个cpu上程序运行多个任务的总体性能。正常情况下,一个任务在单个cpu上顺序进行是最高效率的,但是为什么会出现多线程呢?就是因为一个任务有可能不能顺序进行下去,就是指任务阻塞。当一个任务卡在那里的时候,cpu就是处于浪费状态,所以才会有多线程,当一个任务阻塞的时候,切换到另一个任务。

基本的线程机制

1.创建线程可以使用new Thread(new Runnable())或者new Runnable()或者ExecutorService.excute(new Runnable()),推荐使用后者。线程启动后是为了执行一些任务的,就是我们需要实现的具体功能,这些通常写在继承的run()方法内。

2.当我们的任务需要有返回值的时候,需要继承Callable()String方法。然后调用ExcutorService.submit()方法去执行这个任务

Future
submit(Callable
task);

返回的是Future<T>,T就是我们要的返回值。当然这个任务可能很快就能完成,也会一直完不成,所以在通过future拿返回值时,最后先调用isDone()方法查看是否任务完成

3.线程的休眠和优先级和让步

一般现在都用TimeUnit.*.sleep()

4.后台线程

后台线程派生的线程同样为后台线程,并且如果后台线程的run方法里面有try  cathc  finally,其实finally并不会执行。因为后台线程会在所有非后台线程终止时。后台线程就会马上被关掉。

5.线程的join

一个线程可以在另一个线程上调用join,比如a线程里面进行b.join(),则a线程就停止干活,等到b线程结束后才会进行a,同时也可以设置超时时间。如果b上时间不返回,则可以调用b。interrupt()进行强制中断

6.线程的异常捕获

如果我们在任务中(即run方法中)抛出了异常,使用try{}catch{]是没用的。

例如

1 try {2       ExecutorService exec =Executors.newCachedThreadPool();3 4       exec.execute(new ExceptionThread());//ExceptionThread的run方法直接抛出runtimeException5     } catch(RuntimeException ue) {6       // This statement will NOT execute!7       System.out.println("Exception has been handled!");8     }

解决办法是继承UncaughtExceptionHandler()接口,并将一个线程对象和他绑定,则异常就会被捕获。

并且我们一般new一个Thread,应该是从jdk的DefaultThreadFactory返回的一个runable对象。如果想改变new的thread对象,可以Executors.newCachedThreadPool()时候指定自定义ThreadFactory。//TODO

 

线程竞争

1.当多个线程共同干一件事情,比如说将一个数+2

public int next() {                ++currentEvenValue;                ++currentEvenValue;                return currentEvenValue;  //To change body of implemented methods use File | Settings | File Templates.            }

如果这样做的话,就会有问题,因为看似一个方法能够顺序执行,这篇文章讲述了为什么

如何让这个next方法来保证完成我们期望他完成的任务呢,可以加锁--synchronized

一般我们把这个例子代码中的currentEvenValue叫做共享资源,一般以对象的形式在内存中,但也可能是文件,io端口等等。如果想解决共享资源的问题,一般会在访问这个资源的方法中加上synchronized。

如果一个类有两个synchronized方法  synchronized a()  和synchronized b().那么当你调用一个对象的a方法时,是不能同时进行b方法。

同样,也有另外一种办法,在next()开始的时候new ReenTrantLock(); 有lock()和unlock(),tryLock()方法。  这种方法可以让你在尝试获得锁。如果当前不能,则可以干其他的事情。而synchronized则会一直等待锁资源的释放。

synchronized除了可以锁一个方法,也可以一个代码块,

2.原子性,可视性

原子性就是不会被线程调度机制打断。比如基本类型的赋值(long和dobule除外,64位的读取和写入当作两个32位的,不清楚怎么复现)

volatile来声明一个字段,可以保证他的原子性和可视性,即1个线程对这个字段进行了修改,那么它会刷新到主存。中间不会被打断

3.THreadLocal

Threadlocal不会涉及多个线程的同步,因为他们不会涉及多个线程竞争资源。  这个类经常使用

4.线程的中断

一般我们会通过ExecutorService 的execute()方法来启动线程。如果掉用ExecutorService.shutdownNow()则通过这个ExecutorService启动的所有线程都会被中断

,所以如果我们需要关闭一部分线程而保持其他线程状态,可以通过submit()方法来启动。submit()会返回Future<T>,通过f。cancel(true)方法,则会中断这个线程。不过如果当前线程阻塞在io,网络,或者在synchronized锁的时候,是关闭不掉的。通过关闭底层资源来中断线程。

 

 

转载于:https://www.cnblogs.com/xmngg/p/3617816.html

你可能感兴趣的文章
五个有趣的拓扑变换问题 [转]
查看>>
asp.net中的比较完美的验证码
查看>>
HDU 3277 Marriage Match III(最大流+二分+并查集)
查看>>
FPSMeter – 简单的、可定制主题的 FPS 仪表库
查看>>
Android应用中使用百度地图API定位自己的位置(二)
查看>>
24点经典算法
查看>>
内核及其组成部分
查看>>
.NET 程序集单元测试工具 SmokeTest 应用指南
查看>>
Linux最大线程数限制及当前线程数查询
查看>>
java枚举使用详解
查看>>
什么是Scrum
查看>>
nginx负载均衡的5种策略
查看>>
90%人都不知道:SVN 和 Git 的一些误解和真相
查看>>
Android Studio NDK开发-其他编译选项
查看>>
关于this的全面解析(上)
查看>>
Python相对导入导致SystemError的解决方案(译)
查看>>
Swift 魔法:公开 Getter,隐藏 Setter
查看>>
[分享]iOS开发-UICollectionViewCell 布局
查看>>
NSURLRequestCachePolicy 缓存策略
查看>>
如何理解LXC与Docker之间的主要区别
查看>>