Java并发机制(对Thinking in java的理解)

1、基础:

  1、什么时候出现线程安全问题?

    在多线程编程中,可能会出现多个线程同时访问一个资源(共享资源)的情况,由于每个线程的过程不可控,导致程序实际运行结果与愿望相违背。

  2、如何解决线程安全问题?

    一般而言,所有的并发模式都采用“序列化访问临界资源”的方案:即在同一时刻,只允许一个线程访问临界资源。

    上述方式也称为:同步互斥访问:在访问临界资源的代码前加一个所,访问完以后释放锁。java中有两种方式实现互斥同步:synchronizedLock

2、synchronized实现方式 

  1、synchronized加锁的三种方式及对应原理:

    (1):synchronized修饰普通方法,锁范围是实例范围:所有的对象都自动含有单一的锁(监视器monitor),调用对象中的synchronized方法时,该对象会被加锁,该对象内的其他加锁方法不能被其他线程访问。如:  Person(){synchronized eat(){} synchronized run()};当线程thread1调用该对象的eat时,thread2不能调用run方法; 

      底层原理:如:以常量池中ACC_SYNCHRONIZED标示符,为访问标识,方法调用时,先检查该访问标志是否被设置,若是,则线程先获取monitor,方法执行完释放monitor。

public synchronized void method(){
   System.out.println("Hello Word") ;    
}

     (2):synchronized修饰static方法,锁范围是类范围:所有的Person类对象都会被锁住。

线程1和线程2的eat1方法只能被一个调用。而eat2方法是实例同步的,线程1锁住对象1,线程2锁住对象2,eat2可以被线程1和线程2同步调用。

public class Person implements Runnable{
     static int apple=0;
       //锁住当前的Class类对象,即Person类。 
     public static synchronized void eat1(){
             i++;//非原子性操作;
     } 
    //锁住当前实例,即只对Person的一个实例对象加锁;
public synchronized void eat2(){
        i++;
   }
}

    (3): synchronized修饰同步代码块,所范围是实例对象:有时对整个方法进行加锁,显得不必要,同时也会降低执行效率,需要的仅仅是对方法中某几步操作进行加锁。

public class Person{
    private int apple=0;
    public void eat(){
        synchronized(this){
            apple++;
        }
    }
}  

  注:一个线程可以获取多次该对象的锁:如对象o1调用eat方法,而eat又调用了该对象的run方法,jvm会自动递增加锁的次数,每次离开一个synchronized方法,计数减一,一直到0。

       注:synchronized的优化等内容见以后整理(待补充。。。)。

3、Local的实现方式(java.util.concurrent.locks包下Local接口)

  1、有了synchronized为什么还要使用Local?

    (1)synchronized的缺陷:synchronized只有两种执行结果[1]执行完释放锁。[2]异常,jvm释放锁。但是当一个持有锁的线程被阻塞(如IO),那么其他线程也只能等。再比如,多线程同时读取文件时,不会造成冲突,但是为了避免读写冲突,读操作也被synchronized,效率很低。

    (2)使用Local能解决上述问题,而且,Local是java中的一个类(接口),实现多种方法来获取处理锁;synchronized是java语言内置关键字,无别功能。

   2、Local的使用方式:

  (1):方法内声明并lock方法外声明,方法内lock

public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
     
    public void insert(Thread thread) {
        Lock lock = new ReentrantLock();  //注Lock在方法内,是局部变量
        lock.lock();
        try {
            System.out.println(thread.getName()+"得到了锁");
            for(int i=0;i<5;i++) {
                arrayList.add(i);
            }
        } catch (Exception e) {
            // TODO: handle exception
        }finally {
            System.out.println(thread.getName()+"释放了锁");
            lock.unlock();
        }
    }
}
Thread-0得到了锁
Thread-1得到了锁
Thread-0释放了锁
Thread-1释放了锁
//线程0获取锁不影响线程1的获取
 
public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    private Lock lock = new ReentrantLock();    //注意这个地方
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
     
    public void insert(Thread thread) {
        if(lock.tryLock()) {
            try {
                System.out.println(thread.getName()+"得到了锁");
                for(int i=0;i<5;i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }finally {
                System.out.println(thread.getName()+"释放了锁");
                lock.unlock();
            }
        } else {
            System.out.println(thread.getName()+"获取锁失败");
        }
    }
}
//顺序获取
 

    

  

    

  

猜你喜欢

转载自www.cnblogs.com/whtblog/p/8874564.html
今日推荐