一.Thread API:
setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
首先要了解什么是Thread.UncaughtExceptionHandler,默认来说当线程出现未捕获的异常时,会中断并抛出异常,抛出后的动作只有简单的堆栈输出。如:
public class ThreadTest{ public static void main(String[] args) throws Exception{ Thread t1=new Thread(new Runnable(){ public void run(){ int a=1/0; } }); t1.start(); } }
那么代码运行到int a=1/0;就会报错:
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero at yiwangzhibujian.ThreadTest$1.run(ThreadTest.java:11) at java.lang.Thread.run(Thread.java:662)
这时候如果设置了Thread.UncaughtExceptionHandler,那么处理器会将异常进行捕获,捕获后就可以对其进行处理:
public class ThreadTest{ public static void main(String[] args) throws Exception{ Thread t1=new Thread(new Runnable(){ public void run(){ int a=1/0; } }); t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){ @Override public void uncaughtException(Thread t,Throwable e){ System.out.println("线程:"+t.getName()+"出现异常,异常信息:"+e); } }); t1.start(); } }
那么当线程抛出异常后就可以对其抓取并进行处理,最终结果如下:
线程: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时,怎么对线程中断进行处理。因此,代码可以改成:
@Override public void run(){ while(!Thread.currentThread().isInterrupted()){ //执行某些任务 } } --------------------------------------------------------- @Override public void run(){ //耗时较长步骤1 if(Thread.currentThread().isInterrupted()) return; //耗时较长步骤2 if(Thread.currentThread().isInterrupted()) return; //耗时较长步骤3 }
3.stop()、suspend()、resume()为什么不建议使用:
stop方法会立即中断线程,虽然会释放持有的锁,但是线程的运行到哪是未知的,假如在具有上下文语义的位置中断了,那么将会导致信息出现错误,比如:
@Override public void run(){ try{ //处理资源并插入数据库 }catch(Exception e){ //出现异常回滚 } }
如果在调用stop时,代码运行到捕获异常需要回滚的地方,那么将会因为没有回滚,保存了错误的信息。
而suspend会将当前线程挂起,但是并不会释放所持有的资源,如果恢复线程在调用resume也需要那个资源,那么就会形成死锁。当然可以通过你精湛的编程来避免死锁,但是这个方法具有固有的死锁倾向。所以不建议使用。其他暂停方法为什么可用:
- wait方法会释放锁,所以不会有死锁问题
- sleep方法虽然不释放锁,但是它不需要唤醒,在使用的时候已经指定想要的睡眠时间了。
二、线程安全基本知识:
首先应该记住以下基本点,先背下来也无妨:
- 同一时间一个锁只能被一个线程持有
- 调用对象的wait()和notify()前必须持有它
- wait()和notify()必须和synchronized一起使用
三.ThreadLocal详解:
ThreadLocal这个对象就是为多线程而生的,没有了多线程ThreadLocal就没有存在的必要了。可以将任何你想在每个线程独享的对象放置其中,并在任何时候取出来。
使用ThreadLocal不会导致内存泄漏。
public class ThreadLocalUse{ //创建一个静态的ThreadLocal对象,当做仓库,存储每个线程自己的资源 //此处用Object来代替,可以使连接Connection private static ThreadLocal
对比ThreadLocal和synchronized同步机制:
很多人都会对这两个对象进行比对,我也谈一下我自己的想法。
使用synchronized是为了将多条语句进行原子化操作,比说对于递增操作i++,任意一个线程在执行代码时都要保证别的线程不能执行这个代码,否则就会产生脏数据,使用synchronized可以避免这一点。
而使用ThreadLocal就是给每个线程存储对象用的。既然每个线程使用了自己的对象,没有了竞争就不会出现多线程相关的问题。
四:线程锁synchronized和Lock和volatile和Condition :
多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制。这是Java并发编程中必须要理解的一个知识点。其实使用起来还是比较简单,但是一定要理解。
有几个概念一定要牢记:
- 加锁必须要有锁
- 执行完后必须要释放锁
- 同一时间、同一个锁,只能有一个线程执行
synchronized : 特点是自动释放锁,作用在方法时自动获取锁,任意对象都可做为锁,它是最常用的加锁机制,锁定几行代码。
当一个线程获取到锁的时候,其它线程只能等待这个线程释放锁。(执行完代码释放,或者程序发生异常释放);如果被阻塞了,没有释放锁,其它线程就会一直等待,浪费资源。
如下:
//--------同步方法1 public synchronized void test(){ //一段代码 } //--------同步方法2,锁某个对象 private Object lock=new Object(); public void test2(){ synchronized(lock){ } }
- 作用于普通方法获得当前对象锁,等价于synchronized(this)
- 作用于静态方法获得类锁,等价于synchronized(类.class)
Lock : 的特点是,必须自己创建锁(锁类型已经指定为Lock的实现类,不能使用其它对象),必须自己释放锁。代码结构如下:
Lock lc = new Lock(); lc.lock(); try { // 执行代码 } finally { lc.unlock(); }
//注意一定要在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