博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA多线程深度解析
阅读量:6093 次
发布时间:2019-06-20

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

hot3.png

后台线程

所谓的后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此当所有的非后台线程结束时,程序也就终止了,同时会杀死所有后台线程。反过来说,只要有任何非后台线程(用户线程)还在运行,程序就不会终止。后台线程在不执行finally子句的情况下就会终止其run方法。后台线程创建的子线程也是后台线程    main 是非后台线程

setDaemon(true);  必须在线程start(); 方法前执行   isDaemon() 来判断一个线程是否是守护线程  

在Daemon线程中产生的新线程也是Daemon的

带返回值的线程   Callable  

 Future f1 = pool.submit();    call()方法   线程阻塞到 所有线程都 返回结果

线程本地缓存 ThreadLocal

ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的  

实际是放到当前线程的ThreadLocalMap这个map中

线程安全

1.即有synchronized关键字修饰的方法。

    由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,

内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

代码如:

    public synchronized void save(){}

2  即有synchronized关键字修饰的语句块。

    被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

    代码如:

    synchronized(object){

}

3.使用特殊域变量(volatile)实现线程同步

当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值       

volatile保证了 可见性 但不保证原子行

              private volatile int account = 100;

4.使用重入锁实现线程同步

 ReentrantLock() : 创建一个ReentrantLock实例 

        lock() : 获得锁 

        unlock() : 释放锁

5.wait()  notify()

Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行

调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的

除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。

和 suspend() 和 resume()功能类似  不过后者不会释放锁 

注 :Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放

CountDownLatch

CountDownLatch是减计数方式,计数==0时释放所有等待的线程;CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放所有等待的线程。

CountDownLatch当计数到0时,计数无法被重置;CyclicBarrier计数达到指定值时,计数置为0重新开始。
CountDownLatch每次调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响;CyclicBarrier只有一个await()方法,调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞

线程池

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

Executors 静态方法

newSingleThreadExecutor   创建一个单线程的线程池

newFixedThreadPool         创建固定大小的线程池

newCachedThreadPool        创建一个可缓存的线程池

newScheduledThreadPool    创建一个大小无限的线程池

ThreadPoolExecutor的完整构造方法的签名是:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

 

corePoolSize:核心池的大小

maximumPoolSize:线程池最大线程数     

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止

unit:参数keepAliveTime的时间单位

workQueue:一个阻塞队列

threadFactory:线程工厂,主要用来创建线程

handler:表示当拒绝处理任务时的策略,有以下四种取值

常用方法

execute()

submit()

shutdown()

shutdownNow()

线程异常捕获:

 

线程代码不能抛出任何checked异常。所有的线程中的checked异常都只能被线程本身消化掉当

线程代码抛出运行级别异常之后,线程会中断。

在thread.start()之前 设置 异常回调

thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

            @Override

            public void uncaughtException(Thread t, Throwable e) {

                System.out.println("线程"+t.getName()+"--异常信息");

                e.printStackTrace();

            }

       })

线程池捕获异常

 

而在线程池中却比较特殊。默认情况下,线程池 java.util.concurrent.ThreadPoolExecutor 会Catch住所有异常  

 

通过覆盖ThreadPoolExecutor.afterExecute 方法,我们才能捕获到任务的异常

        protected void afterExecute(Runnable r, Throwable t) {

                super.afterExecute(r, t);

          

            }

转载于:https://my.oschina.net/zhushihui/blog/660964

你可能感兴趣的文章
使用excel 展现数据库内容
查看>>
C#方法拓展
查看>>
MySql.Data.dll的版本
查看>>
Linux系统磁盘管理
查看>>
hdu 2191 (多重背包+二进制优化)
查看>>
home.php
查看>>
neo4j---删除关系和节点
查看>>
redis分布式锁redisson
查看>>
什么样的企业可以称之为初创企业?
查看>>
Python爬虫之BeautifulSoup
查看>>
《HTML 5与CSS 3权威指南(第3版·下册)》——第20章 使用选择器在页面中插入内容...
查看>>
如何判断自己适不适合做程序员?这几个特点了解一下
查看>>
newinstance()和new有什么区别
查看>>
android下载封装类
查看>>
[node] 用 node-webkit 开发桌面应用
查看>>
Nginx访问控制和虚拟主机
查看>>
report widget not working for external users
查看>>
windows phone 摄像头得到图片是旋转90°
查看>>
Linux--sed使用
查看>>
没有显示器的情况下安装和使用树莓派
查看>>