多线程2.0

来源:互联网 发布:mac可以玩dota2 编辑:程序博客网 时间:2024/05/16 10:10

synchronize同步原理:

 所有对象都默认有一个单一的锁,JVM实时监控每个对象上面的锁情况,当第一次进入synchronized代码块时,就会在该代码块的对象锁上加一,当还没离开同步代码块时,其他任务线程访问该代码块时,发向对象上的锁不是0,就无法获得对象锁而处于等待阻塞状态,直到上一个任务离开时,对象锁会自动减一,直到为0了,下一个任务线程才有机会获得对象锁而执行任务


线程中的资源共享:
当多个线程访问同一共享资源时,如果不加以同步处理,很有可能使他们访问出错;例如,把你餐桌上的食物是共享资源,你正伸手去拿食物,突然食物不见了,这就造成了共享资源混乱;java提供给我们的处理的办法就是上锁:synchronized,使用该关键字把你需要的同步方法包起来,则以后有线程要访问你这段代码,就必须获得该对象实例的同步锁(此锁是java内部对象生成的,隐式提供给我们的,该类的其他对象访问这段代码还是可以的;如果同步的方法是静态方法,则要获取该类的同步锁才行),使用方法如下:
public void test() {     ...     synchronized(this) {          // todo your code     }     ...} 此时,其效果等同于public synchronized void test() {     // todo your code}



 
和同步锁相关的三个方法:wait()/notify()/sleep()
wait()表示当前线程释放同步锁,进入线程等待池,是否能够CPU,其他线程可以抢占此同步锁
notify()唤醒因为调用wait()方法而进入休眠的线程,从而使该剧线程有机会再次获得同步锁
sleep()同样也是使线程进入休眠,释放CPU,但是不释放同步锁
wait和notify必须要在synchronized代码块中才调用


与上面对应locked的方式对应有:

await() / signal()和signalAll();并且signal能实现公平的通知,而notify是随机唤醒某个线程


java5提供了新的同步方法lock和unlock:
虽然代码量比synchronized多,但控制精度更高,更准确;并发量大的时候是个不错的选择;使用也更加灵活
使用方法:
class LockTest extends TestTemplate{         ReentrantLock lock=new ReentrantLock();         public LockTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){             super( _id, _round, _threadNum, _cb);         }         /**        * synchronized关键字不在方法签名里面,所以不涉及重载问题        */        @Override        long getValue() {             try{                 lock.lock();                 return super.countValue;             }finally{                 lock.unlock();             }         }         @Override        void sumValue() {             try{                 lock.lock();                 super.countValue+=preInit[index++%round];             }finally{                 lock.unlock();             }         }     } 



使用lock方法的好处:
   1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
     线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
     如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
     如果使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
 
    ReentrantLock获取锁定与三种方式:
    a)  lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
    b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
    c)tryLock(long timeout,TimeUnit unit),   如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
    d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
 
2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
 
3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;


原子性:
即不可被中断的操作属性,要么做完,要么不做;java的基本类型除了64位的double/long类型除外,其他的类型操作都是原子性的;
Java5提供了一些原子性变量类,对他操作时可以保证原子性的操作,AtomicInteger/AtomicLong/AtomicReference;优化代码时使用,一般情况下还是用synchronzied和显示同步锁lock来同步;


volatile:
被volatile修饰的域,那么它会告诉编译器,在编译此段域的时候,对域的读取和写入不要用一些缓存来优化,我直接读取此域的内存地址上的数据;如果多个任务同时访问同一个域,并且至少有一个是写入操作,那么这个域可以设置为volatile;


临界区:
多个线程同时访问方法内的部分代码,而不是全部代码,将这段代码抽离出来进行同步处理,这段代码叫临界区
0 0
原创粉丝点击