Java并发编程-总结-21-33

二十一、死锁

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务。

死锁的发生必须满足以下四个条件:

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁

二十二、异常

1、方法例如sleep() 和join()会抛出InterruptedException 异常,告诉调用者另一个线程已经中断该线程. 在大多数情况下,这样做是为了让当前线程停止当前计算和意外地结束。因此忽略这种异常,仅仅捕获例外和记录在控制台或日志文件通常不是处理这种异常的适当方法. 不过这异常的问题是Runnable接口的方法run() 不允许run()抛出任何异常. 所以重新抛出并没有帮助. 所以这意味着run() 的实现本身必须处理受检异常,这往往导致其被捕获和被忽视。

2、一个不受检查的异常有可能从run() 方法逃逸. 在这种情况下, 该线程被Java虚拟机停止. UncaughtExceptionHandler 作为例外处理程序的接口,通过注册其实例, 来捕获此异常. 这要么是在线程本身没有特定处理,通过调用静态方法Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)告诉JVM使用所提供的处理程序, 或者通过调用线程实例本身setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)

3、如果异常没有被捕获,该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理。

二十三、线程的一些方法

join()方法 : 等待一个线程的完成

yield()方法:该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会,对静态方法Thread.yield() 的调用暗示调度器当前线程愿意释放处理器。调度程序是可以忽略这个暗示,因为它是没有定义哪个线程将得到处理器在调用Thread.yield()之后, 它甚至可能发生当前线程成为“下一步”执行线程

holdsLock():检测一个线程是否拥有锁,它返回true如果当且仅当当前线程拥有某个具体对象的锁

二十四、原子操作

1、我们怎么理解原子操作?

一个原子操作就是要么完全执行或根本不执行的操作.

2、语句i++是否原子性?

不,一个整数型变量的增加包含一个以上的操作。首先我们得到i的现值,然后增加,最后存储新的值。当前线程执行这个策略,可能会在任何这些三步骤之间中断,因此这不是原子操作.

3、什么操作是原子操作?
Java语言提供一些原子性的基本操作,因此可以被用来确保并发线程总是看到相同的值的:
  • 参考变量和原始变量(除了long和double)的读写操作
  • 所有声明为volatile变量的读写操作
二十五、对象传递及不可变类

线程间传递对象时,你将注意到这些物体在同一时间不受两个线程操纵。一个例子是一个实现的键/值对的Map被两个并发线程修改。为了避免并发修改,你可以设计一个对象是不可变的。

为了实现一个不可变类你必须遵守的?

  • 所有属性应是final和private.
  • 应该有没有setter方法.
  • 类本身应声明final为了防止子类违反不变原理.
  • 如果属性是不是一个原始类型,但对另一个对象的引用:
  • 不应该有一个公开getter方法可以直接调用.
  • 不要改变引用的对象(或者至少改变这些引用不会暴露给对象的客户).

二十六、ThreadLocal

ThreadLocal是Java里一种特殊的变量。每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。它是为创建代价高昂的对象获取线程安全的好方法,比如你可以用ThreadLocal让SimpleDateFormat变成线程安全的,因为那个类创建代价高昂且每次调用都需要创建不同的实例所以不值得在局部范围使用它,如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。

首先,通过复用减少了代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。线程局部变量的另一个不错的例子是ThreadLocalRandom类,它在多线程环境中减少了创建代价高昂的Random对象的个数,因为不同线程之间共享内存, ThreadLocal 提供了一种方式分别为每个线程来存储和检索值.ThreadLocal 的实现为每个线程独立存储和检索,当在相同的ThreadLocal, 实例线程A存储值A1和线程B存储值B1的值的值,线程A1在从ThreadLocal实例检索值以后,线程B检索值B1。

实例ThreadLocal 可以用来在应用中传递信息而不需要通过从方法到方法. 例子是在安全/登录信息,放在ThreadLocal 实例中这样可以每个方法中访问. 另一个使用案例将信息放到一般对象,所有的方法都可以直接访问而不用排队访问。

二十七、CAS操作
CAS是compare-and-swap的缩写,意味着处理器提供了一个单独的指令,只有提供的值等于当前值,才更新寄存器的值。CAS操作可以避免同步时线程可以通过提供其当前值和CAS操作新值更新价值。如果另一个线程同时更新值,该线程的值不等于当前值,更新操作失败。然后这个线程读取新的值并再次尝试.在包java.util.concurrent.atomic 的SDK类像AtomicInteger或AtomicBoolean 内部使用CAS操作来实现并行策略.

二十八、同步

关键字synchronized什么目的下使用,当你必须实现资源的专用访问,像一些静态值或某些文件的引用,工作于专用资源的代码就可以使用synchronized块构造方法不能同步。

二十九、volatile 变量和atomic 变量有什么不同

Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用volatile修饰count变量那么count++ 操作就不是原子性的。而AtomicInteger类提供的atomic方法可以让这种操作具有原子性如getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行
相似操作

三十、有没有简单的方法来创建任意Collection, List 或Map的同步实例吗?

共用类的Collections 提供了方法synchronizedCollection(Collection),synchronizedList(List)和synchronizedMap(Map) ,给支持的实例返回一个线程安全的collection/list/map.



三十一、volatile 变量

volatile是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。volatile变量可以保证下一个读取操作会在前一个写操作之后发生。

三十二、线程饥饿

具有较低优先级的线程得到比高优先级线程更少的执行时间。当优先级低的线程执行一个长期持久的计算,它可能发生这些线程没有得到足够的时间准时完成他们的计算。他们似乎“饿死”了因为高优先级线程窃取他们的计算时间.

三十三、为什么wait, notify 和notifyAll这些方法不在thread类里面?

一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值