精华内容
下载资源
问答
  • 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。 例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。   public class Foo {   private int...
    Java线程:线程的同步与锁
     
     
     
    一、同步问题提出
     
    线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。
    例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。
     
    public  class Foo { 
         private  int x = 100; 

         public  int getX() { 
             return x; 
        } 

         public  int fix( int y) { 
            x = x - y; 
             return x; 
        } 
    }
     
    public  class MyRunnable  implements Runnable { 
         private Foo foo =  new Foo(); 

         public  static  void main(String[] args) { 
            MyRunnable r =  new MyRunnable(); 
            Thread ta =  new Thread(r,  "Thread-A"); 
            Thread tb =  new Thread(r,  "Thread-B"); 
            ta.start(); 
            tb.start(); 
        } 

         public  void run() { 
             for ( int i = 0; i < 3; i++) { 
                 this.fix(30); 
                 try { 
                    Thread.sleep(1); 
                }  catch (InterruptedException e) { 
                    e.printStackTrace(); 
                } 
                System.out.println(Thread.currentThread().getName() +  " : 当前foo对象的x值= " + foo.getX()); 
            } 
        } 

         public  int fix( int y) { 
             return foo.fix(y); 
        } 
    }
     
    运行结果:
    Thread-A : 当前foo对象的x值= 40 
    Thread-B : 当前foo对象的x值= 40 
    Thread-B : 当前foo对象的x值= -20 
    Thread-A : 当前foo对象的x值= -50 
    Thread-A : 当前foo对象的x值= -80 
    Thread-B : 当前foo对象的x值= -80 

    Process finished with exit code 0
     
    从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。
     
    如果要保持结果的合理性,只需要达到一个目的,就是将对Foo的访问加以限制,每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。
     
    在具体的Java代码中需要完成一下两个操作:
    把竞争访问的资源类Foo变量x标识为private;
    同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
     
    二、同步和锁定
     
    1、锁的原理
     
    Java中每个对象都有一个内置锁
     
    当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
     
    当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
     
    一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
     
    释放锁是指持锁线程退出了synchronized同步方法或代码块。
     
    关于锁和同步,有一下几个要点:
    1)、只能同步方法,而不能同步变量和类;
    2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
    3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
    4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
    5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
    6)、线程睡眠时,它所持的任何锁都不会释放。
    7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
    8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
    9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:
        public int fix(int y) {
            synchronized (this) {
                x = x - y;
            }
            return x;
        }
     
    当然,同步方法也可以改写为非同步方法,但功能完全一样的,例如:
        public synchronized int getX() {
            return x++;
        }
        public int getX() {
            synchronized (this) {
                return x;
            }
        }
    效果是完全一样的。
     
    三、静态方法同步
     
    要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。
    例如:
    public static synchronized int setName(String name){
          Xxx.name = name;
    }
    等价于
    public static int setName(String name){
          synchronized(Xxx.class){
                Xxx.name = name;
          }
    }

     
    四、如果线程不能不能获得锁会怎么样
     
    如果线程试图进入同步方法,而其锁已经被占用,则线程在该对象上被阻塞。实质上,线程进入该对象的的一种池中,必须在哪里等待,直到其锁被释放,该线程再次变为可运行或运行为止。
     
    当考虑阻塞时,一定要注意哪个对象正被用于锁定:
    1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。
     
    2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。
     
    3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
     
    4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。
     
    五、何时需要同步
     
    在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。
     
    对于非静态字段中可更改的数据,通常使用非静态方法访问。
    对于静态字段中可更改的数据,通常使用静态方法访问。
     
    如果需要在非静态方法中使用静态字段,或者在静态字段中调用非静态方法,问题将变得非常复杂。已经超出SJCP考试范围了。
     
    六、线程安全类
     
    当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。
     
    即使是线程安全类,也应该特别小心,因为操作的线程是间仍然不一定安全。
     
    举个形象的例子,比如一个集合是线程安全的,有两个线程在操作同一个集合对象,当第一个线程查询集合非空后,删除集合中所有元素的时候。第二个线程也来执行与第一个线程相同的操作,也许在第一个线程查询后,第二个线程也查询出集合非空,但是当第一个执行清除后,第二个再执行删除显然是不对的,因为此时集合已经为空了。
    看个代码:
     
    public  class NameList { 
         private List nameList = Collections.synchronizedList( new LinkedList()); 

         public  void add(String name) { 
            nameList.add(name); 
        } 

         public String removeFirst() { 
             if (nameList.size() > 0) { 
                 return (String) nameList.remove(0); 
            }  else { 
                 return  null
            } 
        } 
    }
     
    public  class Test { 
         public  static  void main(String[] args) { 
             final NameList nl =  new NameList(); 
            nl.add( "aaa"); 
             class NameDropper  extends Thread{ 
                 public  void run(){ 
                    String name = nl.removeFirst(); 
                    System.out.println(name); 
                } 
            } 

            Thread t1 =  new NameDropper(); 
            Thread t2 =  new NameDropper(); 
            t1.start(); 
            t2.start(); 
        } 
    }
     
    虽然集合对象
        private List nameList = Collections.synchronizedList(new LinkedList());
    是同步的,但是程序还不是线程安全的。
    出现这种事件的原因是,上例中一个线程操作列表过程中无法阻止另外一个线程对列表的其他操作。
     
    解决上面问题的办法是,在操作集合对象的NameList上面做一个同步。改写后的代码如下:
    public  class NameList { 
         private List nameList = Collections.synchronizedList( new LinkedList()); 

         public  synchronized  void add(String name) { 
            nameList.add(name); 
        } 

         public  synchronized String removeFirst() { 
             if (nameList.size() > 0) { 
                 return (String) nameList.remove(0); 
            }  else { 
                 return  null
            } 
        } 
    }
     
    这样,当一个线程访问其中一个同步方法时,其他线程只有等待。
     
    七、线程死锁
     
    死锁对Java程序来说,是很复杂的,也很难发现问题。当两个线程被阻塞,每个线程在等待另一个线程时就发生死锁。
     
    还是看一个比较直观的死锁例子:
     
    public  class DeadlockRisk { 
         private  static  class Resource { 
             public  int value; 
        } 

         private Resource resourceA =  new Resource(); 
         private Resource resourceB =  new Resource(); 

         public  int read() { 
             synchronized (resourceA) { 
                 synchronized (resourceB) { 
                     return resourceB.value + resourceA.value; 
                } 
            } 
        } 

         public  void write( int a,  int b) { 
             synchronized (resourceB) { 
                 synchronized (resourceA) { 
                    resourceA.value = a; 
                    resourceB.value = b; 
                } 
            } 
        } 
    }
     
    假设read()方法由一个线程启动,write()方法由另外一个线程启动。读线程将拥有resourceA锁,写线程将拥有resourceB锁,两者都坚持等待的话就出现死锁。
     
    实际上,上面这个例子发生死锁的概率很小。因为在代码内的某个点,CPU必须从读线程切换到写线程,所以,死锁基本上不能发生。
     
    但是,无论代码中发生死锁的概率有多小,一旦发生死锁,程序就死掉。有一些设计方法能帮助避免死锁,包括始终按照预定义的顺序获取锁这一策略。已经超出SCJP的考试范围。
     
    八、线程同步小结
     
    1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
    2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
    3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
    4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
    5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
    6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
    7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
     

    本文出自 “熔 岩” 博客,转载请与作者联系!

    展开全文
  • Java多线程探究-Lock对象锁条件变量

    千次阅读 2017-04-12 18:08:50
    一个线程从盘子取一个苹果,如何保证线程A放一个苹果,线程B就把这个苹果取了,不会出现已经放了好几个了,线程B才一个一个的取,现在限定一个条件,盘子里每次只能放一个苹果,由于两个线程随机执行,不保证线程A...

    Lock锁的条件变量

    设想这样的一种情况,现在有一个盘子,一个线程负责往盘子里放一个苹果,一个线程从盘子取一个苹果,如何保证线程A放一个苹果,线程B就把这个苹果取了,不会出现已经放了好几个了,线程B才一个一个的取,现在限定一个条件,盘子里每次只能放一个苹果,由于两个线程随机执行,不能保证线程A刚放了苹果,线程B就刚好取了。如果用通用的思想的话怎么做呢

    应该是加条件判断,线程A每次放的时候,判断盘子里是否有苹果,如果有,则不做处理,线程B执行的时候判断是否有一个苹果,有的话,把这个苹果取了。苹果可以用数字表示,0表示盘子没有苹果,1表示有一个苹果,那么线程A做的事情就是加1,线程B做的就是减1,由于是对同一数据同时操作,必须要用锁保证数据安全
    下面是基本的实现

    public class ThreadDemo4 {
        public static void main(String[] args) {
    
            int[] apple = new int[]{0};
            Object obj = new Object();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true){
                        synchronized (obj){
                            if(apple[0] == 0) {
                                apple[0]++;
                                System.out.println("线程 "+Thread.currentThread().getId()+ "  放了一个苹果 "+apple[0]);
                            }else{
                                System.out.println("线程 "+Thread.currentThread().getId()+ " 放苹果,已经有苹果");
                            }
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
    
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true){
                        synchronized (obj){
                            if(apple[0] == 1) {
                                apple[0]--;
                                System.out.println("线程 "+Thread.currentThread().getId()+  " 取了一个苹果 "+apple[0]);
                            }else{
                                System.out.println("线程 "+Thread.currentThread().getId()+ " 取苹果,没有苹果...");
                            }
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
    
                }
            }).start();
    
        }
    }

    打印结果
    这里写图片描述
    从打印结果来看,虽然线程12可以取到苹果,但是并不是都是在线程11刚放入就能取到,从第三行开始,线程12取了好几次都没有取到,这是因为CPU时间片此时被线程12占有,线程11没有执行的时间片,也就不能放苹果了,但是这样的话,会导致资源的消耗,明知没有苹果,那么线程12应该不用再取了,而是等待线程11放了一个苹果后再取,如果线程12发现没苹果了,就不再继续取了,而是等待线程11放苹果,放完之后,你给我发个通知,我再取,由于两个线程都是随机执行的,没法保证按顺序一放一取。怎么才能做到呢?这就是著名的多线程生产者和消费者问题

    Java的Object提供了几个方法,用来做线程件的通信

    public final native void notify(); 唤醒一个正在等待的线程,如果有多个等待的线程,那么会随机唤醒了一个,这些线程唤醒之后继续尝试获得锁的占有权,进入同步块

    public final native void notifyAll(); 唤醒所有等待的线程

    public final void wait() throws InterruptedException 释放锁,进入线程等待池,等待被别的线程notify

    JDK1.5提供了一个对象锁的条件变量,类似Object的wait,nofity,notifyAll
    类Condition的方法
    这里写图片描述
    下面是一个常见的面试题
    如何用两个线程依次打印出100以内的奇数和偶数,一个线程打印奇数,另一个打印偶数,前面说了如果不用wait和notify的话,没法控制线程的顺序执行和条件执行,很可能一个线程打印了几次,另外一个才打印一次
    现在使用Condition条件变量来实现

    class Obj {
        public int state = 1;
    }
    
    class ThreadA implements Runnable {
    
        private int numA = 0;
        private Obj obj;
        private Lock lock;
        private Condition condition;
    
        public ThreadA(Obj obj, Lock lock, Condition condition) {
            this.obj = obj;
            this.lock = lock;
            this.condition = condition;
        }
    
        @Override
        public void run() {
            while (numA < 100) {
                lock.lock();
                try {
                    if (obj.state != 1) {
                       condition.await();
                    } else {
                        System.out.println(Thread.currentThread().getName() + " >>> " + numA);
                        Thread.sleep(100);
                        numA += 2;
                        obj.state = 2;
                        condition.signal();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
    
            }
        }
    }
    
    class ThreadB implements Runnable {
    
        private int numB = 1;
        private Obj obj;
        private Lock lock;
        private Condition condition;
    
        public ThreadB(Obj obj, Lock lock, Condition condition) {
            this.obj = obj;
            this.lock = lock;
            this.condition = condition;
        }
    
        @Override
        public void run() {
            while (numB < 100) {
                lock.lock();
                try {
                    if (obj.state != 2) {
                       condition.await();
                    } else {
                        System.out.println(Thread.currentThread().getName() + " >>> " + numB);
                        Thread.sleep(100);
                        numB += 2;
                        obj.state = 1;
                        condition.signal();
                    }
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
    
        }
    }
    
    public class ThreadDemo2 {
        public static void main(String[] args) {
    
            Obj obj = new Obj();
            Lock lock = new ReentrantLock();
            Condition d1 = lock.newCondition();
            Runnable a = new ThreadA(obj, lock, d1);
            Runnable b = new ThreadB(obj, lock, d1);
            Thread t1 = new Thread(a, "Thread-A");
            Thread t2 = new Thread(b, "Thread-B");
            t1.start();
            t2.start();
        }
    }

    输出结果
    这里写图片描述
    这样就实现了顺序打印

    总结:如果需要让多个线程按条件顺序执行,就需要使用锁对象的wait,notify方法
    关于生成者消费者模型,看我的另外一篇博客

    展开全文
  • Java多线程----java 对象锁

    万次阅读 2011-09-27 17:37:14
    在并发环境下,解决共享资源冲突问题时,可以考虑使用锁机制。 1. 对象锁 ...如果一个对象被解锁,其计数变为0。在任务(线程)第次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在

          在并发环境下,解决共享资源冲突问题时,可以考虑使用锁机制。

    1. 对象锁

          所有对象都自动含有单一的锁。
         JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。
         只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。
         每当任务离开一个synchronized方法,计数递减,当计数为0的时候,锁被完全释放,此时别的任务就可以使用此资源。

    2. 类锁

          对于同步静态方法/静态变量互斥体,由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只由一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。一旦一个静态变量被作为synchronized block的互斥体。进入此同步区域时,都要先获得此静态变量的对象锁。

          由上述同步静态方法引申出一个概念,那就是类锁。其实系统中并不存在什么类锁。当一个同步静态方法被调用时,系统获取的其实就是代表该类的类对象的对象锁

          可以尝试用以下方式获取类锁

    synchronized (xxx.class) {...}
    
    synchronized (Class.forName("xxx")) {...}
           若要同时获取两种锁,同时获取类锁和对象锁是允许的,并不会产生任何问题,但使用类锁时一定要注意,一旦产生类锁的嵌套获取的话,就会产生死锁,因为每个class在内存中都只能生成一个Class实例对象。

    3. synchronized同步块

    3.1. 同步到单一对象锁

            当使用同步块时,如果方法下的同步块都同步到一个对象上的锁,则所有的任务(线程)只能互斥的进入这些同步块。
            Resource1.java演示了三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,虽然这些同步块处在不同的方法中,但由于是同步到同一个对象(当前对象 synchronized (this)),所以对它们的方法依然是互斥的。
    Resource1.java

    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
     
    public class Resource1 {
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public static void main(String[] args) {
           final Resource1 rs = new Resource1();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    Thread-1:not synchronized in g()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()

    3.2. 同步到多个对象锁

            Resource1.java演示了三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,这些同步块处在不同的方法中,并且是同步到三个不同的对象(synchronized (this),synchronized(syncObject1),synchronized (syncObject2)),所以对它们的方法中的临界资源访问是独立的。
    Resource2.java

    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
     
    public class Resource2 {
        private Object syncObject1 = new Object();
        private Object syncObject2 = new Object();
     
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           synchronized (this) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           synchronized (syncObject1) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           synchronized (syncObject2) {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           }
        }
     
        public static void main(String[] args) {
           final Resource2 rs = new Resource2();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    main:synchronized in h()
    Thread-1:not synchronized in g()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()

    4. Lock对象锁

          除了使用synchronized外,还可以使用Lock对象来创建临界区。Resource3.java的演示效果同Resource1.java;Resource4.java的演示效果同Resource2.java。
    Resource3.java

    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    public class Resource3 {
        private Lock lock = new ReentrantLock();
     
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           lock.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock.unlock();
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           lock.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock.unlock();
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           lock.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock.unlock();
           }
        }
     
        public static void main(String[] args) {
           final Resource3 rs = new Resource3();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    Thread-1:not synchronized in g()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    Thread-0:synchronized in f()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()
    Thread-1:synchronized in g()

    Resource4.java

    package com.zj.lock;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    public class Resource4 {
        private Lock lock1 = new ReentrantLock();
        private Lock lock2 = new ReentrantLock();
        private Lock lock3 = new ReentrantLock();
     
        public void f() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in f()");
           lock1.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in f()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock1.unlock();
           }
        }
     
        public void g() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in g()");
           lock2.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in g()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock2.unlock();
           }
        }
     
        public void h() {
           // other operations should not be locked...
           System.out.println(Thread.currentThread().getName()
                  + ":not synchronized in h()");
           lock3.lock();
           try {
               for (int i = 0; i < 5; i++) {
                  System.out.println(Thread.currentThread().getName()
                         + ":synchronized in h()");
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
               }
           } finally {
               lock3.unlock();
           }
        }
     
        public static void main(String[] args) {
           final Resource4 rs = new Resource4();
     
           new Thread() {
               public void run() {
                  rs.f();
               }
           }.start();
     
           new Thread() {
               public void run() {
                  rs.g();
               }
           }.start();
     
           rs.h();
        }
    }
    结果:
    Thread-0:not synchronized in f()
    Thread-0:synchronized in f()
    main:not synchronized in h()
    main:synchronized in h()
    Thread-1:not synchronized in g()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()
    Thread-1:synchronized in g()
    Thread-0:synchronized in f()
    main:synchronized in h()

    Thread-1:synchronized in g()

         另外,ReentrantLock可定时和可轮询的锁获取模式由tryLock方法实现。

    public boolean tryLock(); //等同于tryLock(0, TimeUnit.SECONDS),不停询问是否可获取锁
    public boolean tryLock(long timeout,
                           TimeUnit unit)
                    throws InterruptedException    //timeout - 等待锁的时间,unit - timeout 参数的时间单位 

    5. synchronized和lock的区别:

          Lock 的锁定是通过代码实现的,而 synchronized 是在 JVM 层面上实现的。

          synchronized 在锁定时如果方法块抛出异常,JVM 会自动将锁释放掉,不会因为出了异常没有释放锁造成线程死锁。但是 Lock 的话就享受不到 JVM 带来自动的功能,出现异常时必须在 finally 将锁释放掉,否则将会引起死锁。

          在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。

          ReentrantLock:

          ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

          Atomic:

          和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。

          关于synchronized和lock的详细区别请看http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html

    展开全文
  • java多线程(二)锁对象

    千次阅读 多人点赞 2015-08-17 08:24:53
    在上篇博客中,我们讨论了Race Condition现象以及它产生的原因,现在我们知道它是不好的种现象了,那么我们有什么方法避免它呢。最直接有效的方式就是放弃多线程,直接改为使用单线程但操作数据,但是这是不优雅...

    转载请注明出处:http://blog.csdn.net/xingjiarong/article/details/47679007
    在上一篇博客中,我们讨论了Race Condition现象以及它产生的原因,现在我们知道它是不好的一种现象了,那么我们有什么方法避免它呢。最直接有效的方式就是放弃多线程,直接改为使用单线程但操作数据,但是这是不优雅的,因为我们知道有时候,多线程有它自己的优势。在这里我们讨论两种其他的方法——锁对象和条件对象。

    锁对象

    java SE5.0之后为实现多线程的互斥引入了ReentrantLock类。ReentrantLock类一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

    ReentrantLock类有两种构造方法:

    构造方法

    一、不带公平参数的构造方法

    private ReentrantLock lock = new ReentrantLock();

    默认的是非公平锁,这种锁不会根据线程等待时间的长短来优先调度线程。

    这样就构造了一个锁对象lock。

    二、带公平参数的锁对象

    private ReentrantLock lock = new ReentrantLock(true);

    此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。

    公平锁和非公平锁的区别:

    与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。

    使用方法

    class X {
        private final ReentrantLock lock = new ReentrantLock();
    
        // 其他变量的定义
    
        public void m() { 
         lock.lock();  // 当试图获得锁时,如果锁已经被别的线程占有,那么该线程会一直被阻塞,直到获得锁
         try {
           // 处理数据
         } finally {
           lock.unlock(); //释放锁
         }
       }
    }

    首先为大家介绍一下,ReentrantLock类的两个最常用的方法:

    lock()
    获得锁对象,如果该锁对象没有被其他线程占有,那么可以立刻获得锁,并执行接下来的处理;如果锁对象已经被其他对象占有,那么该线程就会被阻塞在请求锁的对象的操作上,直到其他的线程释放锁,该线程得到锁,才能继续的向下执行。

    unlock()
    释放锁,已经获得锁对象的线程在操作完数据后要释放锁,以便其他的线程重新获得锁来执行自己的操作,否则所有的试图获得锁的线程都不能继续向下执行。

    使用方法简单明了,就是在执行各个线程都要操作相同数据的代码之前请求锁,在finally语句中释放锁,为什么要在finally中释放锁呢,这是因为如果try语句块中有语句发生异常,则会直接跳过try中所有的剩余代码包括unlock(),所以锁对象就不能得到释放,其他的线程也不能继续向下执行,导致程序不能继续执行,我们在finally中释放锁,这样就能保证一定可以将锁释放掉,不管获得锁的线程是不是正确的执行结束。

    现在来使用这个方法修改一下我们上一篇博客中的代码:

    import java.util.concurrent.locks.ReentrantLock;
    
    class MyThread implements Runnable {
        /**
         * 计算类型,1表示减法,其他的表示加法
         */
        private int type;
        /**
         * 锁对象
         */
        private static ReentrantLock lock = new ReentrantLock();
    
        public MyThread(int type) {
            this.type = type;
        }
    
        public void run() {
    
            if (type == 1)
                for (int i = 0; i < 10000; i++) {
    
                    lock.lock();
    
                    Test.num--;
    
                    lock.unlock();
    
                }
            else
                for (int i = 0; i < 10000; i++) {
    
                    lock.lock();
    
                    Test.num++;
    
                    lock.unlock();
                }
    
        }
    }
    
    public class Test {
    
        public static int num = 1000000;
    
        public static void main(String[] args) {
    
            Thread a = new Thread(new MyThread(1));
            Thread b = new Thread(new MyThread(2));
    
            a.start();
            b.start();
    
            /*
             * 主线程等待子线程完成,然后再打印数值
             */
            try {
                a.join();
                b.join();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(num);
        }
    
    }
    

    再多运行几次,是不是结果都是正确的呢。

    现在,我们来解释一下,什么是可重入的锁。ReentrantLock类中有一个计数器,用来表示一个线程获取锁的数量,初始值为0,当一个线程获得锁时,该值被置为1,可重入的意思就是,已经获得锁的线程还可以继续调用同一个锁所保护的方法,也就是再一次获得锁,当再一次获得锁时,ReentrantLock中的计数器就加1,每释放一次锁,计数器就减1,当计数器减为0的时候,这个线程才是真正的释放了这个锁。

    我们接着讨论另外一个非常重要的问题。ReentrantLock类是依赖于创建它的类的对象。什么意思呢,就是说如果两个线程同时访问同一个ReentrantLock对象的lock()方法保护的方法时,OK,这是没有问题的,锁对象会成功的保护数据操作不会出错。但是如果两个线程同时访问ReentrantLock类的不同对象的被lock()保护的方法,那么这两个线程是不会相互影响的,也就是说lock()方法这时不能保证数据的正确性。

    我们来看一下上边那个代码的这一部分:

    Thread a = new Thread(new MyThread(1));
    Thread b = new Thread(new MyThread(2));
    

    这里建立了两个对象a和b,所以他们每个类都有自己的ReentrantLock对象,这就是我们上边所说的ReentrantLock类的不同对象,这样如果两个线程分别操作a和b的数据,lock方法是不会有效的。

    不信我们试试看这个代码:

    import java.util.concurrent.locks.ReentrantLock;
    
    class MyThread implements Runnable {
        /**
         * 计算类型,1表示减法,其他的表示加法
         */
        private int type;
        /**
         * 锁对象
         */
        private ReentrantLock lock = new ReentrantLock();
    
        public MyThread(int type) {
            this.type = type;
        }
    
        public void run() {
    
            if (type == 1)
                for (int i = 0; i < 10000; i++) {
    
                    lock.lock();
    
                    Test.num--;
    
                    lock.unlock();
    
                }
            else
                for (int i = 0; i < 10000; i++) {
    
                    lock.lock();
    
                    Test.num++;
    
                    lock.unlock();
                }
    
        }
    }
    
    public class Test {
    
        public static int num = 1000000;
    
        public static void main(String[] args) {
    
            Thread a = new Thread(new MyThread(1));
            Thread b = new Thread(new MyThread(2));
    
            a.start();
            b.start();
    
            /*
             * 主线程等待子线程完成,然后再打印数值
             */
            try {
                a.join();
                b.join();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println(num);
        }
    
    }
    

    注意这里的代码和上边的代码并不一样,唯一的区别就在于声明ReentrantLock对象时前边是否加了static,这里是没有static修饰,再运行几次,是不是结果是不正确的呢。

    为什么会这样呢?因为如果被static修饰,那么两个线程就是共用的同一个lock对象,如果不被static修饰,那么每个线程就是使用的它自己的lock对象,所以不会static修饰就会出现错误。

    在下一篇博客里,我会为大家介绍java条件对象,希望与大家一起学习一起进步,请大家继续关注我的博客,如果大家支持我的话,就顶我一下吧。

    展开全文
  • Java多线程-线程同步(对象锁

    千次阅读 2015-08-26 16:54:23
    Java线程同步是用来解决线程间共享资源的。在传统的单线程程序中,程序都是有序的按照程序猿写好的指定的流程运行的,因此对于一些资源,比如某个变量的内存读写顺序是固定的,因此不会出现问题。  但是在多线程...
  • c++线程中的

    万次阅读 多人点赞 2017-06-26 12:29:21
    这个是在中兴面试中被面试问到的一个题“你知道线程中的自旋么?”,我当时一脸懵逼,不知道。回来后整理下,在这里对线程中的进行一个学习。 线程之间的有:互斥、条件、自旋、读写、递归。一般而...
  • 1、synchronized(obj):可以任意指定对象.2、synchronized(this):当前对象:当一个类加到内存时,常量池有一个地址直接指向当前正在执行的对象. 3、public synchronized void run():当前对象(this),这是实...
  • 创建状态:创建一个线程实例 Thread thread = new Thread(); 就绪状态:在调用start()方法后,线程获取了除CPU的其他资源,处于就绪状态 执行状态:线程获取CPU使用权,run方法开始执行 阻塞状态:运行中的线程由于...
  • 关键字synchronized取得的锁都是对象锁,而不是把段代码(方法)当做锁,哪个线程先执行synchronized关键字的方法,那个线程就持有该方法所属对象的锁(Lock),两个对象线程获得的就是两个不同的锁,他们互不...
  • 线程锁

    千次阅读 2010-12-13 13:27:00
    · C#线程锁(上)本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过本篇的介绍对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多...
  • 原文:  ... ...   浅谈java内存模型 ... 不同的平台,内存模型是不一样的,但是jvm的...其实java的多线程并发问题最终都会反映在java的内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改。总
  • 看到这个问题第一反应是不懂,然后查询了网上的一些说法,感觉略有不一。细看之下发现大家对这个问题的...然后,多个线程访问这个类的两个方法也有不同的形式,例如访问这个类的两个方法是通过同一个类的实例对象...
  • 学习 java 多线程时,最头疼的知识点之一就是 java 中的了,什么互斥、排它、自旋、死锁、活锁等等,细分的话可以罗列出 20 种左右的,光是看着这些名字就足以让人望而却步了,更别说一个个去理解它们的...
  • 但在最近查线上日志的时候偶然发现,有一个业务场景下,分布式偶尔会失效,导致有多个线程同时执行了相同的代码。 我们经过初步排查,定位到是因为在这段代码中间调用了第三方的接口导致。 因为...
  • java线程安全篇之synchronized 多个线程个锁(二)
  • 1、并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的...synchronized或者Lock:保证同一个时刻只有一个线程获取执行代码,释放之前把最新的值刷新到主内存,实现可见性。 3...
  • 线程常用的几个方法汇总

    千次阅读 2018-02-19 17:52:14
    解说编程最近学习并发编程遇到不少问题,就顺手总结了有关多线程几个常用的方法sleep()sleep()方法属于Thread类,主要的作用是让当前线程停止执行,把cpu让给其他线程执行,但不会释放对象锁和监控的状态,到了...
  • Java停止一个线程种方法

    千次阅读 2015-11-07 22:26:11
    Java中停止一个线程有三种方法,分别是stop,interrupt和设置标志位,我们依次来看一下这三种方法。 首先不推荐使用stop方法,原因有两点: 1、原则上只要一调用thread.stop()方法,线程就会立即停止,并抛出...
  • JUC多线程:synchronized 机制原理

    千次阅读 多人点赞 2021-08-26 08:53:31
    synchronized 通过当前线程持有对象锁,从而拥有访问权限,而其他没有持有当前对象锁线程无法拥有访问权限,保证在同一时刻,只有一个线程可以执行某个方法或者某个代码块,从而保证线程安全。synchronized 锁机制...
  • sqlite读写线程模式

    万次阅读 2019-06-14 18:06:40
    sqlite读写 SQLite3总共有三种事务类型:BEGIN [ DEFERRED /IMMEDIATE / EXCLUSIVE ] TRANSCATION,提供以下五种的文件状态,按锁的级别依次是:UNLOCKED / SHARED / RESERVERD / PENDING / EXCLUSIVE。 1). ...
  • 【C/C++】多线程中的

    千次阅读 2018-11-27 15:47:40
    线程之间的有:互斥、条件、自旋、读写、递归。一般而言,的功能越...任何是一个线程都要使用互斥互斥访问任务队列,以避免多个线程同时访问任务队列以发生错乱。 在某一时刻,只有一个线程可以获...
  • 类锁 synchronized对象锁 和 Lock对象锁

    千次阅读 2017-03-18 18:45:49
    1. 对象锁所有对象都自动含有单一的锁。JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会...
  • 线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的独立运行的基本单位。线程的划分尺度小于进程,这使得多线程程序的并发性高;进程在执行时通常拥有独立的内存单元,而线程之间可以共享内存。 ...
  • 答案是一把锁 ???? 代码示例: public class Test { public synchronized void method1(){ // do something } public synchronized void method2(){ ...对象锁是在一个类的对象上加的的...
  • 线程之互斥与死锁

    万次阅读 2017-06-05 14:00:31
    互斥一个二元变量,其状态为开锁(允许0)和上(禁止1),将某个共享资源与某个特定互斥在逻辑上绑定(要申请该资源必须先获取)。 (1)访问公共资源前,必须申请该互斥,若处于开锁状态,则申请到锁对象,并...
  • 线程安全与优化

    千次阅读 2017-04-17 22:54:49
    线程安全与优化线程安全《Java Concurrency In Practice》的作者Brian Goetz对“线程安全”有一个比较恰当的定义:“当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要...
  • 线程锁+线程池+线程同步等

    千次阅读 2019-04-28 16:20:39
    1、并发编程三要素? 1)原子性:原子性指的是一个或者多个操作,要么全部执行并且在执行的...synchronized或者Lock:保证同一个时刻只有一个线程获取执行代码,释放之前把最新的值刷新到主内存,实现可见性。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 153,402
精华内容 61,360
关键字:

一个线程能获得几个对象锁