一.Thread API:

setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

  首先要了解什么是Thread.UncaughtExceptionHandler,默认来说当线程出现未捕获的异常时,会中断并抛出异常,抛出后的动作只有简单的堆栈输出。如:

  1. public class ThreadTest{
  2. public static void main(String[] args) throws Exception{
  3. Thread t1=new Thread(new Runnable(){
  4. public void run(){
  5. int a=1/0;
  6. }
  7. });
  8. t1.start();
  9. }
  10. }

  那么代码运行到int a=1/0;就会报错:

  1. Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
  2. at yiwangzhibujian.ThreadTest$1.run(ThreadTest.java:11)
  3. at java.lang.Thread.run(Thread.java:662)

  这时候如果设置了Thread.UncaughtExceptionHandler,那么处理器会将异常进行捕获,捕获后就可以对其进行处理:

  1. public class ThreadTest{
  2. public static void main(String[] args) throws Exception{
  3. Thread t1=new Thread(new Runnable(){
  4. public void run(){
  5. int a=1/0;
  6. }
  7. });
  8. t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){
  9. @Override
  10. public void uncaughtException(Thread t,Throwable e){
  11. System.out.println("线程:"+t.getName()+"出现异常,异常信息:"+e);
  12. }
  13. });
  14. t1.start();
  15. }
  16. }

  那么当线程抛出异常后就可以对其抓取并进行处理,最终结果如下:

  1. 线程:Thread-0出现异常,异常信息:java.lang.ArithmeticException: / by zero

  如果自己写线程,那么完全可以在run方法内,将所有代码进行try catch,在catch里做相同的操作。UncaughtExceptionHandler的意义在于不对(或者不能对)原有线程进行修改的情况下,为其增加一个错误处理器。

2.interrupt() 、interrupted() 、isInterrupted()作用:

  • 当线程正常运行时,中断属性设置为true,调用其isInterrupted()方法会返回true。
  • 当线程阻塞时(wait,join,sleep方法),会立即抛出InterruptedException异常,并将中断属性设置为false。此时再调用isInterrupted()会返回false。

  这样就由程序来决定当检测到中断属性为true时,怎么对线程中断进行处理。因此,代码可以改成:

  1. @Override
  2. public void run(){
  3. while(!Thread.currentThread().isInterrupted()){
  4. //执行某些任务
  5. }
  6. }
  7. ---------------------------------------------------------
  8. @Override
  9. public void run(){
  10. //耗时较长步骤1
  11. if(Thread.currentThread().isInterrupted()) return;
  12. //耗时较长步骤2
  13. if(Thread.currentThread().isInterrupted()) return;
  14. //耗时较长步骤3
  15. }

3.stop()、suspend()、resume()为什么不建议使用:

  stop方法会立即中断线程,虽然会释放持有的锁,但是线程的运行到哪是未知的,假如在具有上下文语义的位置中断了,那么将会导致信息出现错误,比如:

  1. @Override
  2. public void run(){
  3. try{
  4. //处理资源并插入数据库
  5. }catch(Exception e){
  6. //出现异常回滚
  7. }
  8. }

  如果在调用stop时,代码运行到捕获异常需要回滚的地方,那么将会因为没有回滚,保存了错误的信息。

  而suspend会将当前线程挂起,但是并不会释放所持有的资源,如果恢复线程在调用resume也需要那个资源,那么就会形成死锁。当然可以通过你精湛的编程来避免死锁,但是这个方法具有固有的死锁倾向。所以不建议使用。其他暂停方法为什么可用:

  • wait方法会释放锁,所以不会有死锁问题
  • sleep方法虽然不释放锁,但是它不需要唤醒,在使用的时候已经指定想要的睡眠时间了。

二、线程安全基本知识:

  首先应该记住以下基本点,先背下来也无妨:

  • 同一时间一个锁只能被一个线程持有
  • 调用对象的wait()和notify()前必须持有它
  • wait()和notify()必须和synchronized一起使用

三.ThreadLocal详解

ThreadLocal这个对象就是为多线程而生的,没有了多线程ThreadLocal就没有存在的必要了。可以将任何你想在每个线程独享的对象放置其中,并在任何时候取出来。

使用ThreadLocal不会导致内存泄漏。

  1. public class ThreadLocalUse{
  2. //创建一个静态的ThreadLocal对象,当做仓库,存储每个线程自己的资源
  3. //此处用Object来代替,可以使连接Connection
  4. private static ThreadLocal<Object> store=new ThreadLocal<Object>();
  5.  
  6. //当每个线程要获取一个线程资源的时候,调用get方法
  7. public Object get(){
  8. //首先去仓库中去寻找
  9. Object obj=store.get();
  10. //如果不存在那就创建一个给线程,并把创建的放到仓库中
  11. if(obj==null){
  12. obj=new Object();
  13. set(obj);
  14. }
  15. return obj;
  16. }
  17.  
  18. //想将线程资源放到仓库调用set方法,
  19. public void set(Object obj){
  20. store.set(obj);
  21. }
  22. }

对比ThreadLocal和synchronized同步机制:

  很多人都会对这两个对象进行比对,我也谈一下我自己的想法。

  使用synchronized是为了将多条语句进行原子化操作,比说对于递增操作i++,任意一个线程在执行代码时都要保证别的线程不能执行这个代码,否则就会产生脏数据,使用synchronized可以避免这一点。

  而使用ThreadLocal就是给每个线程存储对象用的。既然每个线程使用了自己的对象,没有了竞争就不会出现多线程相关的问题。

四:线程锁synchronized和Lock和volatile和Condition :

多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制。这是Java并发编程中必须要理解的一个知识点。其实使用起来还是比较简单,但是一定要理解。

  有几个概念一定要牢记:

  • 加锁必须要有锁
  • 执行完后必须要释放锁
  • 同一时间、同一个锁,只能有一个线程执行

synchronized : 特点是自动释放锁作用在方法时自动获取锁,任意对象都可做为锁,它是最常用的加锁机制,锁定几行代码。

        当一个线程获取到锁的时候,其它线程只能等待这个线程释放锁。(执行完代码释放,或者程序发生异常释放);如果被阻塞了,没有释放锁,其它线程就会一直等待,浪费资源。

        如下:

  1. //--------同步方法1
  2. public synchronized void test(){
  3. //一段代码
  4. }
  5. //--------同步方法2,锁某个对象
  6. private Object lock=new Object();
  7. public void test2(){
  8. synchronized(lock){
  9.  
  10. }
  11. }
  • 作用于普通方法获得当前对象锁,等价于synchronized(this)
  • 作用于静态方法获得类锁,等价于synchronized(类.class)
  1.  

Lock : 的特点是,必须自己创建锁(锁类型已经指定为Lock的实现类,不能使用其它对象),必须自己释放锁。代码结构如下:

  1. Lock lc = new Lock();
  2. lc.lock();
  3. try {
  4. // 执行代码
  5. } finally {
  6. lc.unlock();
  7. }
    //注意一定要在finally中释放锁,保证即便抛出异常也可以释放。

volatile和synchronized的区别:

  • volatile关键字解决的是变量在多个线程之间的可见性(对于用volatile修饰的变量,JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新的);而sychronized关键字解决的是多个线程之间访问共享资源的同步性。
  • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的;
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  • volatile修饰变量适合于一写多读的并发场景,而多写场景一定会产生线程安全问题(因此使用volatile而不是synchronized的唯一安全情况是类中只有一个可变的域)。
  • 因为所有的操作都需要同步给内存变量,所以volatile一定会使线程的执行速度变慢。

synchronized和lock区别:

1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率(读写锁)。

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

Condition在前面并发一章已经介绍过,有兴趣的童鞋可以去看一看。https://www.cnblogs.com/2019lgg/p/11319656.html

多线程学习三:Thread API,ThreadLocal,synchronized,volatile和Condition的更多相关文章

  1. 多线程学习笔记九之ThreadLocal

    目录 多线程学习笔记九之ThreadLocal 简介 类结构 源码分析 ThreadLocalMap set(T value) get() remove() 为什么ThreadLocalMap的键是W ...

  2. Java多线程专题3: Thread和ThreadLocal

    合集目录 Java多线程专题3: Thread和ThreadLocal 进程, 线程, 协程的区别 进程 Process 进程提供了执行一个程序所需要的所有资源, 一个进程的资源包括虚拟的地址空间, ...

  3. 多线程学习-基础( 十一)synchronized关键字修饰方法的简单案例

    一.本案例设计到的知识点 (1)Object的notify(),notifyAll(),wait()等方法 (2)Thread的sleep(),interrupt(). (3)如何终止线程. (4)如 ...

  4. JAVA多线程学习- 三:volatile关键字

    Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个 ...

  5. python多线程学习三

    本文希望达到的目标: 1.服务器端与线程池  (实例demo) 2.并发插入db与线程池(实例demo) 3.线程池使用说明 4.线程池源码解析 一.基于socket的服务器与线程池连接. 1.在i7 ...

  6. java多线程学习三

    本章主要学习线程的静态方法 1.先忙先看一段代码: public class MyThread3 implements Runnable { static { System.out.println(& ...

  7. Java多线程学习(三)---线程的生命周期

    线程生命周期 摘要: 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞 ...

  8. C#多线程学习(三) 生产者和消费者

    前面说过,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数.这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生. ...

  9. NodeJS学习三之API

    Node采用V8引擎处理JavaScript脚本,最大特点就是单线程运行,一次只能运行一个任务.这导致Node大量采用异步操作(asynchronous opertion),即任务不是马上执行,而是插 ...

随机推荐

  1. (转)阿里 RocketMQ 安装与简介

    原文:阿里 RocketMQ 安装与简介 一.简介 官方简介: l  RocketMQ是一款分布式.队列模型的消息中间件,具有以下特点: l  能够保证严格的消息顺序 l  提供丰富的消息拉取模式 l ...

  2. Python爬虫实战教程:爬取网易新闻

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: Amauri PS:如有需要Python学习资料的小伙伴可以加点击 ...

  3. 爬虫selenium中截图

    一.整个页面截图 driver = webdriver.Chrome() driver.get(url) diver.save_screenshot('保存路径') 二.局部截图 driver = w ...

  4. JMeter压测“java.net.SocketException: Socket closed”解决方法

    报错详情: java.net.SocketException: Socket closed at java.net.SocketInputStream.socketRead0(Native Metho ...

  5. 网络协议 2 - IP 地址和 MAC 地址

    了解完网络协议,我们会发现,网络通信的五层模型里,有两个很重要的概念:IP 地址和 MAC 地址. 那么 IP 地址是怎么来的,又是怎么没的?MAC 地址与 IP 地址又有什么区别? 这回答上面问题前 ...

  6. JQuery Ztree 树插件配置与应用小结

    JQuery Ztree 树插件配置与应用小结 by:授客 QQ:1033553122 测试环境 Win7 jquery-3.2.1.min.js 下载地址: https://gitee.com/is ...

  7. 使用MailKit发送带有内嵌图片的邮件且图片不显示成附件

    使用MailKit发送带有内嵌图片的邮件且图片不显示成附件 参考文章:MailKit---发送邮件 注意 在邮件客户端中是否显示内嵌图片为附件依据不同邮件有所不同,暂经测试Outlook和qq不显示为 ...

  8. Python日期计算

    Python源代码如下: # -*- coding: UTF-8 -*- """ 简述:要求输入某年某月某日 提问:求判断输入日期是当年中的第几天? Python解题思路 ...

  9. ansible 流程控制

    ansible 流程控制 使用when判断主机名 - hosts: rsync_server tasks: - name: Install rsyncd Server yum: name: rsync ...

  10. SQL Server查询数据库近期执行的SQL语句

    SELECT TOP 1000        ST.text AS '执行的SQL语句',       QS.execution_count AS '执行次数',       QS.total_ela ...