精华内容
下载资源
问答
  • 透彻理解 Java synchronized 对象锁和类锁的区别

    万次阅读 多人点赞 2017-02-17 14:34:05
    synchronized 加到 static 方法...对象锁和类锁是不同的锁,所以多个线程同时执行这2个不同锁的方法时,是异步的。 在Task2 中定义三个方法 doLongTimeTaskA和doLongTimeTaskB是类锁,而doLongTimeTaskC是对象锁。pub

    synchronized 加到 static 方法前面是给class 加锁,即类锁;而synchronized 加到非静态方法前面是给对象上锁。这两者的区别我用代码来演示下:

    1. 对象锁和类锁是不同的锁,所以多个线程同时执行这2个不同锁的方法时,是异步的。

    在Task2 中定义三个方法 doLongTimeTaskA和doLongTimeTaskB是类锁,而doLongTimeTaskC是对象锁。

    public class Task2 {
    
        public synchronized static void doLongTimeTaskA() {
            System.out.println("name = " + Thread.currentThread().getName() + ", begain");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
        }
    
        public synchronized static void doLongTimeTaskB() {
            System.out.println("name = " + Thread.currentThread().getName() + ", begain");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
        }
    
        public synchronized void doLongTimeTaskC() {
    
            System.out.println("name = " + Thread.currentThread().getName() + ", begain");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("name = " + Thread.currentThread().getName() + ", end");
    
        }
    

    三个线程的代码如下:

    class ThreadA extends Thread{
        
        private Task2 mTask2;
        
        public ThreadA(Task2 tk){
            mTask2 = tk;
        }
        
        public void run() {
            mTask2.doLongTimeTaskA();
        }
    }
    
    class ThreadB extends Thread{
        
        private Task2 mTask2;
        
        public ThreadB(Task2 tk){
            mTask2 = tk;
        }
        
        public void run() {
            mTask2.doLongTimeTaskB();
        }
    }
    
    class ThreadC extends Thread{
        
        private Task2 mTask2;
        
        public ThreadC(Task2 tk){
            mTask2 = tk;
        }
        
        public void run() {
            mTask2.doLongTimeTaskC();
        }
    }
    

    main函数中执行代码如下:

            Task2 mTask2 = new Task2();
            ThreadA ta = new ThreadA(mTask2);
            ThreadB tb = new ThreadB(mTask2);
            ThreadC tc = new ThreadC(mTask2);
            
            ta.setName("A");
            tb.setName("B");
            tc.setName("C");
            
            ta.start();
            tb.start();
            tc.start();
    

    执行的结果如下:

    name = A, begain, time = 1487311199783
    name = C, begain, time = 1487311199783
    name = C, end, time = 1487311200784
    name = A, end, time = 1487311200784
    name = B, begain, time = 1487311200784
    name = B, end, time = 1487311201784
    

    可以看出由于 doLongTimeTaskA和doLongTimeTaskB都是类锁,即同一个锁,所以 A和B是按顺序执行,即同步的。而C是对象锁,和A/B不是同一种锁,所以C和A、B是 异步执行的。(A、B、C代指上面的3中方法)。

    我们知道对象锁要想保持同步执行,那么锁住的必须是同一个对象。下面就修改下上面的来证明:

    Task2.java不变,修改ThreadA 和 ThreadB 如下:

    class ThreadA extends Thread{
        
        private Task2 mTask2;
        
        public ThreadA(Task2 tk){
            mTask2 = tk;
        }
        
        public void run() {
            mTask2.doLongTimeTaskC();
        }
    }
    
    class ThreadB extends Thread{
        
        private Task2 mTask2;
        
        public ThreadB(Task2 tk){
            mTask2 = tk;
        }
        
        public void run() {
            mTask2.doLongTimeTaskC();
        }
    }
    

    main方法如下:

     Task2 mTaska = new Task2();
     Task2 mTaskb = new Task2();
     ThreadA ta = new ThreadA(mTaska );
     ThreadB tb = new ThreadB(mTaskb );
            
            
     ta.setName("A");
     tb.setName("B");
            
     ta.start();
     tb.start();
          
    

    结果如下:

    name = A, begain, time = 1487311905775
    name = B, begain, time = 1487311905775
    name = B, end, time = 1487311906775
    name = A, end, time = 1487311906775
    

    从结果看来,对象锁锁的对象不一样,分别是mTaska , mTaskb,所以线程A和线程B调用 doLongTimeTaskC 是异步执行的。

    但是,类锁可以对类的所有对象的实例起作用。只需修改ThradA
    和 ThreadB,main 方法不做改变,修改如下:

    class ThreadA extends Thread{
        
        private Task2 mTask2;
        
        public ThreadA(Task2 tk){
            mTask2 = tk;
        }
        
        public void run() {
            //mTask2.doLongTimeTaskC();
            mTask2.doLongTimeTaskA();
        }
    }
    
    class ThreadB extends Thread{
        
        private Task2 mTask2;
        
        public ThreadB(Task2 tk){
            mTask2 = tk;
        }
        
        public void run() {
           //mTask2.doLongTimeTaskC();
            mTask2.doLongTimeTaskA();
        }
    }
    

    结果如下:

    name = A, begain, time = 1487312239674
    name = A, end, time = 1487312240674
    name = B, begain, time = 1487312240674
    name = B, end, time = 1487312241674
    

    可以看出 在线程A执行完doLongTimeTaskA方法后,线程B才会获得该类锁接着去执行doLongTimeTaskA。也就是说,类锁对所有的该类对象都能起作用。

    总结:

    1. 如果多线程同时访问同一类的 类锁(synchronized 修饰的静态方法)以及对象锁(synchronized 修饰的非静态方法)这两个方法执行是异步的,原因:类锁和对象锁是2中不同的锁。
    2. 类锁对该类的所有对象都能起作用,而对象锁不能。

    以后文章会同步到个人公众号,欢迎关注交流
    在这里插入图片描述

    展开全文
  • synchronized 加到 static 方法前面是给class 加锁,即类锁;而synchronized 加到非静态方法前面是给对象上锁。多线程访问对象锁是异步的,访问类锁是同步的。详情查看:点击打开链接...

    synchronized 加到 static 方法前面是给class 加锁,即类锁;而synchronized 加到非静态方法前面是给对象上锁。

    多线程访问对象锁是异步的,访问类锁是同步的。

    详情查看:点击打开链接

    展开全文
  • java的内置:每个java对象都可以用做一个实现同步的,这些成为内置。线程进入同步代码块或方法的时候会自动获得该,在退出同步代码块或方法时会释放该。 获得内置的唯一途径就是进入这个的保护的...

    终于搞明白synchronized的作用了,献上一篇,如下:

    java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。
    获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

    java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,
    如果B线程不释放这个锁,那么A线程将永远等待下去。

    java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,
    或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,
    所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,
    它只是用来帮助我们理解锁定实例方法和静态方法的区别的。

     

    public class TestSynchronized 
    {  
        public void test1() 
        {  
             synchronized(this) 
             {  
                  int i = 5;  
                  while( i-- > 0) 
                  {  
                       System.out.println(Thread.currentThread().getName() + " : " + i);  
                       try 
                       {  
                            Thread.sleep(500);  
                       } 
                       catch (InterruptedException ie) 
                       {  
                       }  
                  }  
             }  
        }  
        public synchronized void test2() 
        {  
             int i = 5;  
             while( i-- > 0) 
             {  
                  System.out.println(Thread.currentThread().getName() + " : " + i);  
                  try 
                  {  
                       Thread.sleep(500);  
                  } 
                  catch (InterruptedException ie) 
                  {  
                  }  
             }  
        }  
        public static void main(String[] args) 
        {  
             final TestSynchronized myt2 = new TestSynchronized();  
             Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, "test1"  );  
             Thread test2 = new Thread(  new Runnable() {  public void run() { myt2.test2();   }  }, "test2"  );  
             test1.start();;  
             test2.start();  
    //         TestRunnable tr=new TestRunnable();
    //         Thread test3=new Thread(tr);
    //         test3.start();
        } 
    }
     
    test2 : 4
    test2 : 3
    test2 : 2
    test2 : 1
    test2 : 0
    test1 : 4
    test1 : 3
    test1 : 2
    test1 : 1
    test1 : 0
    public class TestSynchronized 
    {  
        public void test1() 
        {  
             synchronized(TestSynchronized.class) 
             {  
                  int i = 5;  
                  while( i-- > 0) 
                  {  
                       System.out.println(Thread.currentThread().getName() + " : " + i);  
                       try 
                       {  
                            Thread.sleep(500);  
                       } 
                       catch (InterruptedException ie) 
                       {  
                       }  
                  }  
             }  
        }  
        public static synchronized void test2() 
        {  
             int i = 5;  
             while( i-- > 0) 
             {  
                  System.out.println(Thread.currentThread().getName() + " : " + i);  
                  try 
                  {  
                       Thread.sleep(500);  
                  } 
                  catch (InterruptedException ie) 
                  {  
                  }  
             }  
        }  
        public static void main(String[] args) 
        {  
             final TestSynchronized myt2 = new TestSynchronized();  
             Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, "test1"  );  
             Thread test2 = new Thread(  new Runnable() {  public void run() { TestSynchronized.test2();   }  }, "test2"  );  
             test1.start();  
             test2.start();  
    //         TestRunnable tr=new TestRunnable();
    //         Thread test3=new Thread(tr);
    //         test3.start();
        } 
    }
     
    
    test1 : 4
    test1 : 3
    test1 : 2
    test1 : 1
    test1 : 0
    test2 : 4
    test2 : 3
    test2 : 2
    test2 : 1
    test2 : 0
    public class TestSynchronized 
    {  
        public synchronized void test1() 
        {  
                  int i = 5;  
                  while( i-- > 0) 
                  {  
                       System.out.println(Thread.currentThread().getName() + " : " + i);  
                       try 
                       {  
                            Thread.sleep(500);  
                       } 
                       catch (InterruptedException ie) 
                       {  
                       }  
                  }  
        }  
        public static synchronized void test2() 
        {  
             int i = 5;  
             while( i-- > 0) 
             {  
                  System.out.println(Thread.currentThread().getName() + " : " + i);  
                  try 
                  {  
                       Thread.sleep(500);  
                  } 
                  catch (InterruptedException ie) 
                  {  
                  }  
             }  
        }  
        public static void main(String[] args) 
        {  
             final TestSynchronized myt2 = new TestSynchronized();  
             Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, "test1"  );  
             Thread test2 = new Thread(  new Runnable() {  public void run() { TestSynchronized.test2();   }  }, "test2"  );  
             test1.start();  
             test2.start();  
    //         TestRunnable tr=new TestRunnable();
    //         Thread test3=new Thread(tr);
    //         test3.start();
        } 
    }
     
    
    test1 : 4
    test2 : 4
    test1 : 3
    test2 : 3
    test2 : 2
    test1 : 2
    test2 : 1
    test1 : 1
    test1 : 0
    test2 : 0

     

    
     

    上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。

     

    这时如果直接用synchronized修饰调用了so.testsy();代码的方法,那么当某个线程进入了这个方法之后,这个对象其他同步方法都不能给其他线程访问了。假如这个方法需要执行的时间很长,那么其他线程会一直阻塞,影响到系统的性能。如果这时用synchronized来修饰代码块:synchronized(so){so.testsy();},那么这个方法加锁的对象是so这个对象,跟执行这行代码的对象没有关系,当一个线程执行这个方法时,这对其他同步方法时没有影响的,因为他们持有的锁都完全不一样。不过这里还有一种特例,就是上面演示的第一个例子,对象锁synchronized同时修饰方法和代码块,这时也可以体现到同步代码块的优越性,如果test1方法同步代码块后面有非常多没有同步的代码,而且有一个100000的循环,这导致test1方法会执行时间非常长,那么如果直接用synchronized修饰方法,那么在方法没执行完之前,其他线程是不可以访问test2方法的,但是如果用了同步代码块,那么当退出代码块时就已经释放了对象锁,当线程还在执行test1的那个100000的循环时,其他线程就已经可以访问test2方法了。这就让阻塞的机会或者线程更少。让系统的性能更优越。

     

    public class Main {
    	class Inner {
    		private void m4t1() {
    			int i = 5;
    			while (i-- > 0) {
    				System.out.println(Thread.currentThread().getName()
    						+ " : Inner.m4t1()=" + i);
    				try {
    					Thread.sleep(500);
    				} catch (InterruptedException ie) {
    				}
    			}
    		}//m4t1
    
    		private  synchronized void m4t2() {
    			int i = 5;
    			while (i-- > 0) {
    				System.out.println(Thread.currentThread().getName()
    						+ " : Inner.m4t2()=" + i);
    				try {
    					Thread.sleep(500);
    				} catch (InterruptedException ie) {
    				}
    			}
    		}//m4t2
    	}//inner
    
    	private void m4t1(Inner inner) {
    		synchronized (inner) { // 使用对象锁
    			inner.m4t1();
    		}
    	}
    
    	private void m4t2(Inner inner) {
    		inner.m4t2();
    	}
    
    	public static void main(String[] args) {
    		final Main myt3 = new Main();
    		final Inner inner = myt3.new Inner();
    		Thread t1 = new Thread(new Runnable() {
    			public void run() {
    				myt3.m4t1(inner);
    			}
    		}, "t1");
    		Thread t2 = new Thread(new Runnable() {
    			public void run() {
    				myt3.m4t2(inner);
    			}
    		}, "t2");
    		t1.start();
    		t2.start();
    	}
    }
    //t1 : Inner.m4t1()=4
    //t1 : Inner.m4t1()=3
    //t1 : Inner.m4t1()=2
    //t1 : Inner.m4t1()=1
    //t1 : Inner.m4t1()=0
    //t2 : Inner.m4t2()=4
    //t2 : Inner.m4t2()=3
    //t2 : Inner.m4t2()=2
    //t2 : Inner.m4t2()=1
    //t2 : Inner.m4t2()=0
    public class SynObj{
        public synchronized void showA(){
            System.out.println("showA..");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        public void showB(){
            synchronized (this) {
            	System.out.println(this.getClass().getSimpleName());
                System.out.println("showB..");
            }
        }
        
        public void showC(){
            String s="1";
            synchronized (s) {
                System.out.println("showC..");
            }
        }
    }
    public class Main {
        public static void main(String[] args) {
            final SynObj sy=new SynObj();
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    sy.showA();
                }
            }).start();
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    sy.showB();
                }
            }).start();
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    sy.showC();
                }
            }).start();
        }
    }
    
    //showA..
    //showC..
    //SynObj
    //showB..
    

    这段代码的打印结果是,showA…..showC…..会很快打印出来,showB…..会隔一段时间才打印出来,那么showB为什么不能像showC那样很快被调用呢?
      在启动线程1调用方法A后,接着会让线程1休眠3秒钟,这时会调用方法C,注意到方法C这里用synchronized进行加锁,这里锁的对象是s这个字符串对象。
    但是方法B则不同,是用当前对象this进行加锁,注意到方法A直接在方法上加synchronized,这个加锁的对象是什么呢?显然,这两个方法用的是一把锁。
      *由这样的结果,我们就知道这样同步方法是用什么加锁的了,由于线程1在休眠,这时锁还没释放,导致线程2只有在3秒之后才能调用方法B,
    由此,可知两种加锁机制用的是同一个锁对象,即当前对象。 
      另外,同步方法直接在方法上加synchronized实现加锁,同步代码块则在方法内部加锁,很明显,同步方法锁的范围比较大,
    而同步代码块范围要小点,一般同步的范围越大,性能就越差,一般需要加锁进行同步的时候,肯定是范围越小越好,这样性能更好*。

     

    一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。
    另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。 
    二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。 
    三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

    对象锁和类锁可以同时获得,即两种方法可以同时运行,互不影响;

    一种锁方法只能运行同时运行一个,同种其他的锁方法暂时阻塞,普通方法的运行不受限制

    参考:

    http://www.cnblogs.com/wl0000-03/p/5973039.html

    http://www.cnblogs.com/xujingyang/p/6565606.html

    http://blog.csdn.net/h_gao/article/details/52266950

    http://blog.csdn.net/zhenwodefengcaii/article/details/54601098 

     

    更多企业内的技术应用和使用技巧,请移步至我的公众号【程序员实用技能】

    图片

     

    展开全文
  • JAVA Synchronized对象锁和类锁区别(牛逼)

    千次阅读 多人点赞 2019-01-03 18:48:03
    一个类就像一个四合院,四合院的大门叫做构造方法,盖房子必须经过大门,每new一个对象,就表示在四合院里再盖一间新房子,大门上面的,叫做构造,里面每一间房子就是一个实例,每间房子里边都有一个叫做实例...

    一个类就像一个四合院,四合院的大门叫做构造方法,盖房子必须经过大门,每new一个对象,就表示在四合院里再盖一间新房子,大门上面的锁,叫做构造锁,里面每一间房子就是一个实例,每间房子里边都有一个锁叫做实例锁。
    四合院里有厕所,水龙头,洗衣机等等都是公共设施,都叫做static资源)你想获得洗衣机,就需要调用static的洗衣机方法,也就是行为)

    假如一天来了3个人租房子,第一个人租了一间小房子,那么他就获得一个实例(第一间房子),第二个人也租了一间房子,那么也获得一个实例(第二间房子),如果第三个人想租房子,他看第一间房子比较好,采光各方面都不错,他想租这个,但是呢,第一间房子已经租出去了,但是他不讲道理,闯着非要住,第一间房的住户正在家里吃馒头,没有锁门,这个人直接冲进来,说要租房子,看人家吃馒头,他随手直接吃了一个,然后第一个人就不够吃了,于是第一个人给他讲道理,说你没道德(你写代码肯定bug多),但是呢,第二个人根本就不吊他。过了几天第三个人又跑来,看他正在和老婆玩耍,门没锁,就直接冲进来,也想玩耍,然后就被第一个人打出来了,第一个人很扫兴,于是想出来一个办法,每次进屋后就把门锁了,这样第三个人想进来也进不来,就得等着,除非第一个人出来之后,别人才能进去。假如第一个人睡着了(sleep(xxx))外面的人可能就会等好久。 

    有一天房东的老太太嫌烦,不想租房子了,就要求儿子把大门锁了,因此就不能租房子了,租房子必须经过大门,大门都锁了,还租个屁。因此构造方法都锁定了,还实例鸡毛对象。哈哈。


    水龙头和游泳池都是属于当前这个四合院的,因此在java中要想使用 这些公共设施,前提你得告诉程序,你要使用哪个四合院的公共设施,四合院.水龙头 ,使用对象.的方式告诉程序。

    假如说房东不想让这些住户使用水龙头,他就可以给水龙头上一把锁,给水龙头上锁,和给其它房子上锁完全是两个不同的对象,因此,锁定水龙头和锁定实例不互斥。这就是Synchronized对象锁和类锁区别

    展开全文
  • 转载 https://blog.csdn.net/pengweid/article/details/85711970
  • 1.1 synchronized加到static方法是对整个类加锁 public synchronized static void doLongTimeTaskA() { System.out.println("name = " + Thread.currentThread().getName() + ", begain");...
  • java 内置 : 每个 java对象 都可以用做一个实现同步的,这些成为内置。线程进入同步代码块或方法的时候会自动获得该,在退出同步代码块或方法时会释放该。获得内置的唯一途径就是进入这个的保护的...
  • 对象锁(类对象实例): synchronized关键字修饰非静态方法,在多线程访问同一对象实例(可以理解为对该对象的内存加锁)时,才是同步的,访问不同对象就不是同步的,因为不同对象会有不同的内存。 类锁和对象锁是...
  • 透彻理解javaSynchronized对象锁和类锁的区别 java内置锁 ​ java内置锁:每个java对象都可以用做一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法的...
  • java编程中,经常需要用到...java的内置:每个java对象都可以用做一个实现同步的,这些成为内置。线程进入同步代码块或方法的时候会自动获得该,在退出同步代码块或方法时会释放该。获得内置的唯一途
  • java synchronized类锁,对象锁详解(转载)对象锁:1、一个对象中所有方法都上了synchronized,运行的时候就会互斥运行,不会同时运行。以为两个同步代码所需要获得的对象锁都是同一个对象锁。2、一个对象一个方法...
  • java的内置:每个java对象都可以用做一个实现同步的,这些成为内置。线程进入同步代码块或方法的时候会自动获得该,在退出同步代码块或方法时会释放该。获得内置的唯一途径就是进入这个的保护的同步...
  • synchronized 加到 static 方法前面是给class...1.类锁和对象锁是两把不同的锁,多线程执行两个不同锁的方法时是异步的 加锁的类(下面几个此类不变) public class Task2 {  public synchronized static void d...
  • 你能谈谈Javasynchronized 对象锁和类锁的区别 synchronized 加到 static 方法前面是给class 加锁,即类锁;而synchronized 加到非静态方法前面是给对象上锁。 这两者的区别我用代码来演示下 对象锁和类锁是不同...
  • java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,下面看看这个...java的内置:每个java对象都可以用做一个实现同步的,这些成为内置。线程进入同步代码块或方法的时候会自动获得该
  • 1)无论synchronized关键字加在方法上,还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象锁;如果synchronized关键字作用的对象是一个静态方法或一个类,则它取得的锁是对于类及该类对象是同一把锁,即...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,630
精华内容 3,052
关键字:

javasynchronized对象锁

java 订阅