并发编程三

   Lock

      Java5的concurrent包引入的接口: java.util.concurrent.locks,其功能与synchronize类似,在开始代码处调用lock() 方法获取锁,在结尾处调用unlock()释放锁。

public class MainClass
{
    private Lock   lock;
 
    private Object shared;
 
    public void operation()
    {
        lock.lock(); //获得锁,如果无法得到,线程将进入不可中断的等待状态
        try
        {
            operate( shared ); //操作共享资源
        }
        finally
        {
            lock.unlock(); //释放锁
        }
    }
}

 lock接口比synchronize关键字提供了更加细致的控制方法,它提供了一下方法

  1.      lock() : 获取锁
  2.      tryLock() :尝试获取锁,如果未获取锁直接返回false,不进行等待
  3.      tryLock( Long time,TimeUnit unit ) : 尝试获取锁,如果获取不成功进行等待,在等待的时长中获取到锁,返回true,否则返回false 。可以响应中断异常
  4.      lockinterruptibly():尝试获取锁,无法获取锁时进行等待,线程出于等待时,可以调用interrupt方法中断线程的等待过程。
  5.     unlock():释放锁
  6.     newCondition() :创建一个与此锁关联的Condition对象,Condition可以代替Object.wait / signal / notify / notifyAll
  •      可重入锁:如果当先线程已经持有锁L,那么调用另外一个需要该锁的方法时,不需重新的释放锁在获取锁,而是在当前锁的计数上++,退出该方法是在锁的计数上--。这于synchronize关键字的语意一致。不是所有的lock的实现都支持可重入语义,ReentrantLock提供一下方法:
  •     公平锁:尽可能的按照线程的请求顺序来获取锁,即等待时间长的线程先获取锁,这样可以避免饥饿现象出现。new ReentrantLock(true) 可以构建公平锁,它基本上是按照先入先出的顺序来服务锁的请求者。开发者可以实现锁类来实现其他公平锁的策略。synchronize就是非公平锁。
  •     读写锁: 读写锁将锁的临界资源分成连个部分,一个读锁和一个写锁,正因为有了读写锁,才使得多线程下的读操作不会发生冲突。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock是它的实现,readLock()是获取读锁,WriteLock()是获取写锁。在没有写锁的情况下,多个线程可以同时获取读锁,但是当有读锁或者写锁时,线程在获取写锁的时候必须等待其余线程将锁释放掉才能获取。
  •     语句重排:JVM在不影响语义的情况下对语句进行重排。这种重排是不考虑并发的。
private int i;
private int j;
public void set()
{
    //下面两个语句的顺序JVM可以重排
    i = 0;
    j = 1; //该语句可以重排到该方法的任何地方
    if ( i == 0 ) //i = 0的指令必须在这一句前面
        i = 1;
}

 这种指令重排可能导致多线程下结果和预期不符。因此不要依靠代码的顺序来避免同步,典型的反例是双重检查惯例,synchronize关键字可以防止指令重排。

 JSR-133中,volatile语义加强,禁止了volatile变量和普通变量之间的重排序。因此在Java5后,volatile和synchronize具有一样的语义--针对单个变量的读写锁。

Atomic类

   Java5的concurrent包引入了若干atomic类,这些类支持原子的方式进行操作,可以避免同步,提高性能。

   Atomic家族内部实现上使用了一种乐观锁的机制:假设当前线没有被其他线程修个的共享变量,并计算变量和新值,在尝试更新变量时,检查是否有其他线程修个该值,如果有,获取新值,重复上面的计算。

线程的优先级

  1.   线程优先级1-10,数字越大优先级越高,默认的优先级为5
  2.   优先级无法保证线程的执行顺序,只不过优先级高的线程获取cpu执行的几率较大,优先级低的线程并非没有机会获取
  3.   由调度决定,程序中那个线程执行
  4.   thread.setPriority()是用来设置线程优先级的
  5.   设置线程的优先级应该在调用start()方法前
  6.   可以使用常量来设置线程的优先级,如:MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY

yield 和join 方法的区别

   yield() 是cpu当前执行的线程,让出cpu让等待的线程共同竞争cpu,当前线程有可能重现活的cpu使用权,继续执行

   join() 是当前主线程进入waiting状态,等待子线程执行完毕后在进行执行。

线程状态

    Initial : 初始化状态,从线程对象被调用一直到调用start()方法;

    Runnable:线程可运行状态,线程调用start()方法后就处于该状态,JDK提供了好多方法可以让线程改变Runnable状态。处于该状态下的线程不一定占用cpu,某些情况对Runnable状态进行细分,Runnable是等待获取cpu,Running是已经获取了cpu正在执行。

    Blocked:线程阻塞,处于该状态的线程不能运行,因为它在等待某种事件的发生(定时器、I/O)等。下列情况会进入阻塞状态:

           线程等待I/O,如读取socket;

           尝试进入一个被其它线程占据的临界区(Synchronize)受阻

           尝试其它线程占据的Locked受阻

           执行了线程Thread的 sleep()、join() 方法或者Object.wait()方法

      在某些情况下,Blocked状态如下细分:

           Blocked: 等待I/O 线程等待进入临界区;

           Waiting: 调用Object.wait()方法;

           Sleeping:调用Thread.sleep()方法;

     Exiting: 一旦线程从run方法返回,或者线程调用stop方法,就进入该状态。某些情况下也程Dead状态。

           

猜你喜欢

转载自lianpeng0011.iteye.com/blog/2416086