Java synchronized以及ReentrantLock 学习

来源:互联网 发布:js 时间字符串转date 编辑:程序博客网 时间:2024/05/16 15:05

              四月份开始接触android,开始由C转java。对java里的好多还不是很理解。项目间歇期间,学习下java的基础知识。

              synchronized是java的一个关键字。可以用来修饰一个方法或者是java代码块。能够保证在同一个时刻只能有一个线程访问这个方法或者这块代码块。

             当两个并发线程同时访问一个对象的synchronized代码块时,同时只有一个线程可以执行,另外一个线程必须等上一个线程执行完毕才可以执行。以下是一个Demo:

public class Thread1 implements Runnable{

      /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        long tot = 1L << 32;
        System.out.println(tot);
        Thread1 t1=new Thread1();
        Thread ta=new Thread(t1,"A");
        Thread tb=new Thread(t1,"B");
        ta.start();
        tb.start();
        
    }
 
    @Override
    public void run() {
        // TODO Auto-generated method stub
        synchronized(this){
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+" synchronized loop "+i);
                try {
                    Thread.sleep(100);//加上这句话就可以看得很明显。
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
            
        
    }
   
}
         这段代码是加了synchronized的,输出是:

        A synchronized loop 0
        A synchronized loop 1
        A synchronized loop 2
        A synchronized loop 3
        A synchronized loop 4

        B synchronized loop 0
        B synchronized loop 1
        B synchronized loop 2
        B synchronized loop 3
        B synchronized loop 4

       如果把synchronized去掉,输出是:

       A synchronized loop 0
       B synchronized loop 0
       A synchronized loop 1
       B synchronized loop 1
       A synchronized loop 2
       B synchronized loop 2
       A synchronized loop 3
       B synchronized loop 3
       A synchronized loop 4
       B synchronized loop 4

       通过输出可以很明显的看出synchronized的作用。

       当一个线程访问一个对象的synchronized代码块时,另一个线程可以访问这个对象的非synchronized代码块。下面是一个小例子:

public class Thread1{

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        long tot = 1L << 32;
        System.out.println(tot);
        final Thread1 td=new Thread1();
        Thread t1=new Thread(new Runnable(){public void run(){td.sMethod();}},"t1");
        Thread t2=new Thread(new Runnable(){public void run(){td.method();}},"t2");
        t1.start();
        t2.start();
    
        
    }
    
    public void sMethod(){
        synchronized(this){
            int i=5;
            while(i-->0){
                System.out.println(Thread.currentThread().getName()+": "+i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    
    public void method(){
        int i=5;
        while(i-->0){
            System.out.println(Thread.currentThread().getName()+": "+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
   
}

      输出是:

      t1: 4
      t2: 4
      t1: 3
      t2: 3
      t1: 2
      t2: 2
      t1: 1
      t2: 1
      t2: 0
      t1: 0

      另外需要注意的是当一个线程访问这个对象的synchronized方法或者代码块时,其他线程对这个对象的所有synchronized方法或者代码块的访问都将被阻塞。
      在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成 员变量的访问。 

      synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,甚至有可能引起死锁。

      无论synchronized是加在对象上还是方法上,它取得的锁是对象,而不是把锁加在方法或者代码块上。

      每个对象只有一个锁与之关联。

    

      以下转自:http://blog.csdn.net/quqi99/article/details/5298017

      ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。以下是作者的一个例子:


public class Test {
    //是用ReentrantLock,还是用synchronized
    public static boolean useSynchronized = true;
    public static void main(String[] args) {
        IBuffer buff = null;
        if(useSynchronized){
            buff = new Buffer();
        }else{
            buff = new BufferInterruptibly();   
        }
        final Writer writer = new Writer(buff);
        final Reader reader = new Reader(buff);
        writer.start();
        reader.start();
        new Thread(new Runnable() {
            public void run() {
                long start = System.currentTimeMillis();
                for (;;) {
                    // 等5秒钟去中断读
                    if (System.currentTimeMillis() - start > 5000) {
                        System.out.println("不等了,尝试中断");
                        reader.interrupt();
                        break;
                    }

                }

            }
        }).start();
    }
}

interface IBuffer{
    public void write();
    public void read() throws InterruptedException;
}
class Buffer implements IBuffer{
    private Object lock;

    public Buffer() {
        lock = this;
    }

    public void write() {
        synchronized (lock) {
            long startTime = System.currentTimeMillis();
            System.out.println("开始往这个buff写入数据…");
            for (;;)// 模拟要处理很长时间
            {
                if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)
                    break;
            }
            System.out.println("终于写完了");
        }
    }

    public void read() {
        synchronized (lock) {
            System.out.println("从这个buff读数据");
        }
    }
}

class BufferInterruptibly implements IBuffer{

    private ReentrantLock lock = new ReentrantLock();

    public void write() {
        lock.lock();
        try {
            long startTime = System.currentTimeMillis();
            System.out.println("开始往这个buff写入数据…");
            for (;;)// 模拟要处理很长时间
            {
                if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)
                    break;
            }
            System.out.println("终于写完了");
        } finally {
            lock.unlock();
        }
    }

    public void read() throws InterruptedException{
        lock.lockInterruptibly();// 注意这里,可以响应中断
        try {
            System.out.println("从这个buff读数据");
        } finally {
            lock.unlock();
        }
    }

}

class Writer extends Thread {

    private IBuffer buff;

    public Writer(IBuffer buff) {
        this.buff = buff;
    }

    @Override
    public void run() {
        buff.write();
    }

}

class Reader extends Thread {

    private IBuffer buff;

    public Reader(IBuffer buff) {
        this.buff = buff;
    }

    @Override
    public void run() {

        try {
            buff.read();
        } catch (InterruptedException e) {
            System.out.println("我不读了");  
        }

        System.out.println("读结束");

    }
}

       这个模拟第一种机制,RenntrantLock不去响应B线程的中断,五秒之后的输出是:

       开始往这个buff写入数据…
       不等了,尝试中断

       如果将 public static boolean useSynchronized = true;改为: public static boolean useSynchronized = false;则模拟的是第二种机制,ReentrantLcok去响应B线程的中断。

五秒之后的输出是:

      开始往这个buff写入数据…
      不等了,尝试中断
      我不读了
      读结束

      结果很明显。

     以上是参考网上一些内容,这方面的知识还要继续加深理解。


原创粉丝点击