什么是死锁,如何避免死锁
1,避免多次锁定。尽量避免同一个线程对多个 Lock 进行锁定。例如上面的死锁程序,主线程要对 A、B 两个对象的 Lock 进行锁定,副线程也要对 A、B 两个对象的 Lock 进行锁定,这就埋下了导致死锁的隐患。
2,具有相同的加锁顺序。如果多个线程需要对多个 Lock 进行锁定,则应该保证它们以相同的顺序请求加锁。比如上面的死锁程序,主线程先对 A 对象的 Lock 加锁,再对 B 对象的 Lock 加锁;而副线程则先对 B 对象的 Lock 加锁,再对 A 对象的 Lock 加锁。这种加锁顺序很容易形成嵌套锁定,进而导致死锁。如果让主线程、副线程按照相同的顺序加锁,就可以避免这个问题。
3,使用定时锁。程序在调用 acquire() 方法加锁时可指定 timeout 参数,该参数指定超过 timeout 秒后会自动释放对 Lock 的锁定,这样就可以解开死锁了。
4,死锁检测。死锁检测是一种依靠算法机制来实现的死锁预防机制,它主要是针对那些不可能实现按序加锁,也不能使用定时锁的场景的。
实现Runnable接口和Callable接口的区别
实现Runnable和实现Callable区别
1, run() call()
2, run()没有返回值 call()可以有返回值
3, run()方法签名上不可以声明异常 call() 可以
4, 启动线程,获取运行信息时implements Callable 需要借助FutureTask类
wait 和sleep的区别
1,sleep方法是Thread类里面的静态方法,wait 是Object类中的方法
2,sleep可以在非同步块中执行,wait只能在同步块中执行,否则就会报IllegalMonitorStateException
3, sleep方法在进行线程阻塞时,不释放锁,wait方法在线程阻塞时,释放锁,在没有被其他线程唤醒时一直阻塞
notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,
但不是马上得到锁,因为锁还在别人手里,别人还没释放。
如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁
懒汉和饿汉的区别
1,单例类加载时,饿汉速度慢(因为实体化对象) ,懒汉相对快
2,获取单例对象时,饿汉速度快,懒汉速度慢(获取时实例化对象)
3,运行期,对象在在线时间,早点8点 启动,饿汉直接实例化对象,一直存在于内存,中午12点时,使用懒汉获取对象(12-20),到晚上20点关闭
start 和run方法区别
start 启动线程方法
run 启动多线程执行的业务方法,run方法也可以作为普通方法调用,失去多线程的意义
interrupted() 和isInterrupted() 区别
判断线程是否中断
interrupted 静态方法 测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false
isInterrupted 实例方法 查询其它线程的中断状态且不会改变中断状态标识,如果连续两次调用该方法,则第二次调用将返回 true
多线程中Condition
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用
,await() signal() siganlall()代替object中wait() notify() notifyall()
ReadWriteLock和synchronized 区别
ReadWriteLock 读写锁 读锁 写锁 多线程可以共享读锁 写锁,独占锁 多线程不能共享
读读共享 读写互斥 写写互斥
synchronized 没有读写锁一说
submit 和execute 方法,都可以启动线程
1 参数 execute只支持Runnable接口的实现类 submit方法既支持Runnable还支持Callable
2 返回值 execute没有返回值 submit可以调用callable支持返回值 并且借助Future 可以获取返回值
3 异常处理 execute 不支持 submit支持异常处理,需要调用future.get()方法抛出异常