`
ninghq
  • 浏览: 11641 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

深入理解synchronized关键字的用法

    博客分类:
  • java
阅读更多

 

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

 
互斥锁:内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。
 
synchronized既是内置锁也是互斥锁
 
synchronized三种修饰
第一、修饰普通方法
代码示例:
public class TestSynchronized {
    
	public synchronized void out() throws InterruptedException {
	  System.out.println("test开始..");  
      try {  
          Thread.sleep(5000L);  
      } catch (InterruptedException e) {  
          e.printStackTrace();  
      }  
      System.out.println("test结束..");
	}
	
	public static void main(String[] args) {
		TestSynchronized testSync = new TestSynchronized();
		TestSynchronized testSync2 = new TestSynchronized();
		
		new Thread(() -> {
			try {
				testSync.out();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();
		
		new Thread(() -> {
			try {
				testSync2.out();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();
		
	}

}
 控制台输出:
test开始..
test开始..
test结束..
test结束..
 很明显,synchronized修饰普通方法的时候,锁住的是对象的实例,代码示例中,testSync 和testSync2分别都是TestSynchronized对象的实例,他们两个都可以同时进入synchronized修饰的普通方法,所以得出,synchronized修饰普通方法的时候,锁住的是对象的实例。
第二、修饰静态方法
代码示例:
public class TestSynchronized {

    	public static synchronized void staticOut() throws InterruptedException {
		long startTime = System.currentTimeMillis();  
		System.out.println("test开始..");  
	      try {  
	          Thread.sleep(5000L);  
	      } catch (InterruptedException e) {  
	          e.printStackTrace();  
	      }  
	      System.out.println("test结束..");
	      long endTime = System.currentTimeMillis();
	      System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms");
		}
	
	public static void main(String[] args) {
		TestSynchronized testSync = new TestSynchronized();
		TestSynchronized testSync2 = new TestSynchronized();
		
		new Thread(() -> {
			try {
				testSync.staticOut();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();
		
		new Thread(() -> {
			try {
				testSync2.staticOut();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();
		
	}

}
 控制台输出:
test开始..
test结束..
线程Thread-0程序运行时间:5000ms
test开始..
test结束..
线程Thread-1程序运行时间:5000ms
 可以看出,synchronized修饰静态方法的时候,起到了锁的作用,线程分别获得锁后才进入静态方法中,但是尽量不要使用synchronized修饰静态方法,因为它锁住的是整个类,也就是说,在整个类中的其他synchronized修饰的方法都会被锁住。
示例代码如下:
public class TestSynchronized {
   
	public static synchronized void staticOut() throws InterruptedException {
		long startTime = System.currentTimeMillis();  
		System.out.println("test开始..");  
	      try {  
	          Thread.sleep(5000L);  
	      } catch (InterruptedException e) {  
	          e.printStackTrace();  
	      }  
	      System.out.println("test结束..");
	      long endTime = System.currentTimeMillis();
	      System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms");
	}
	
	
	public static synchronized void staticOut2() throws InterruptedException {
		long startTime = System.currentTimeMillis();  
		System.out.println("test2开始..");  
	      try {  
	          Thread.sleep(5000L);  
	      } catch (InterruptedException e) {  
	          e.printStackTrace();  
	      }  
	      System.out.println("test2结束..");
	      long endTime = System.currentTimeMillis();
	      System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms");
	}
	
	public static void main(String[] args) {
		TestSynchronized testSync = new TestSynchronized();
		TestSynchronized testSync2 = new TestSynchronized();
		
		new Thread(() -> {
			try {
				testSync.staticOut();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();
		
		new Thread(() -> {
			try {
				testSync2.staticOut2();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}).start();
		
	}

}
 控制台输出:
test开始..
test结束..
线程Thread-0程序运行时间:5000ms
test2开始..
test2结束..
线程Thread-1程序运行时间:5001ms
 可以看出,线程Thread-0进入synchronized修饰的静态方法staticOut()的时候,这个类就被锁住了,线程Thread-1无法获得锁,只能等待锁的释放后才能进入方法staticOut2()。所以使用synchronized修饰静态方法需要慎重。
 
第三、修饰代码块
示例代码:
public class TestSynchronized {

	private Object lock = new Object();
	public void lockOut(){
		synchronized(lock){
			long startTime = System.currentTimeMillis();  
			System.out.println("test开始..");  
		    try {  
		        Thread.sleep(5000L);  
		    } catch (InterruptedException e) {  
		        e.printStackTrace();  
		    }  
		    System.out.println("test结束..");
		    long endTime = System.currentTimeMillis();
		    System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms");
			
		}
	}
	
	public static void main(String[] args) {
		TestSynchronized testSync = new TestSynchronized();
		TestSynchronized testSync2 = new TestSynchronized();
		
		new Thread(() -> {
			testSync.lockOut();
		}).start();
		
		new Thread(() -> {
			testSync2.lockOut();
		}).start();
		
	}
}
 控制台输出:
test开始..
test开始..
test结束..
test结束..
线程Thread-0程序运行时间:5000ms
线程Thread-1程序运行时间:5000ms
 synchronized修饰代码块时,锁住的是一个对象 synchronized (lock) 即synchronized后面括号里的内容,因为两个对象创建了两个不同对象实例lock,所以两个对象的线程都可以同时进入synchronized修饰代码块。如果想锁住synchronized修饰的代码块,只需要确定synchronized后面括号里锁住同一
对象即可,常用的方法如下:
1、synchronized锁这个类对应的Class对象。
实例代码:
public class TestSynchronized {
    
//	private Object lock = new Object();
	public void lockOut(){
		synchronized(TestSynchronized.class){
			long startTime = System.currentTimeMillis();  
			System.out.println("test开始..");  
		    try {  
		        Thread.sleep(5000L);  
		    } catch (InterruptedException e) {  
		        e.printStackTrace();  
		    }  
		    System.out.println("test结束..");
		    long endTime = System.currentTimeMillis();
		    System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms");
			
		}
	}
	
	public static void main(String[] args) {
		TestSynchronized testSync = new TestSynchronized();
		TestSynchronized testSync2 = new TestSynchronized();
		
		new Thread(() -> {
			testSync.lockOut();
		}).start();
		
		new Thread(() -> {
			testSync2.lockOut();
		}).start();
		
	}

}
 控制台输出:
test开始..
test结束..
线程Thread-0程序运行时间:5001ms
test开始..
test结束..
线程Thread-1程序运行时间:5002ms
 让synchronized锁这个类对应的Class对象这种方法实现了全局锁的效果,和synchronized修饰静态方法一样(static synchronized方法也是相当于全局锁),整个类就被锁住了,所以此方法一样需要慎重使用。
2、创建一个单例对象,锁住的是该单例对象,单例对象只有一个实例。
public class TestSynchronized {
 
	private volatile static Object lock = new Object(); 
	public void lockOut(){
		synchronized(lock){
                        System.out.println("指针地址:" + lock.toString());  
			long startTime = System.currentTimeMillis();  
			System.out.println("test开始..");  
		    try {  
		        Thread.sleep(5000L);  
		    } catch (InterruptedException e) {  
		        e.printStackTrace();  
		    }  
		    System.out.println("test结束..");
		    long endTime = System.currentTimeMillis();
		    System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms");
			
		}
	}
	
	public static void main(String[] args) {
		TestSynchronized testSync = new TestSynchronized();
		TestSynchronized testSync2 = new TestSynchronized();
		
		new Thread(() -> {
			testSync.lockOut();
		}).start();
		
		new Thread(() -> {
			testSync2.lockOut();
		}).start();
		
	}

}
 控制台输出:
指针地址:java.lang.Object@ce407e7
test开始..
test结束..
线程Thread-0程序运行时间:1000ms
指针地址:java.lang.Object@ce407e7
test开始..
test结束..
线程Thread-1程序运行时间:1001ms
 保证了单例对象lock 的实例唯一性,synchronized锁住同一个固定对象,从控制台上可以看出,访问代码块的对象指针地址是一样的。
 
3、访问该代码块的对象唯一
示例代码:
public class TestSynchronized {
	
//	private volatile static Object lock = new Object(); 
	
	public void lockOut(){
		synchronized(this){
                        System.out.println("指针地址:" + this.toString());  
			long startTime = System.currentTimeMillis();  
			System.out.println("test开始..");  
		    try {  
		        Thread.sleep(1000);  
		    } catch (InterruptedException e) {  
		        e.printStackTrace();  
		    }  
		    System.out.println("test结束..");
		    long endTime = System.currentTimeMillis();
		    System.out.println("线程"+Thread.currentThread().getName() + "程序运行时间:" + (endTime - startTime) + "ms");
			
		}
	}
	
	public static void main(String[] args) {
		TestSynchronized testSync = new TestSynchronized();
		
			new Thread(() -> {
				testSync.lockOut();
			}).start();
			
			new Thread(() -> {
				testSync.lockOut();
			}).start();
		}

}
 控制台输出:
指针地址:com.test.test.TestSynchronized@1dca18a4
test开始..
test结束..
线程Thread-0程序运行时间:1000ms
指针地址:com.test.test.TestSynchronized@1dca18a4
test开始..
test结束..
线程Thread-1程序运行时间:1000ms
 synchronized后面括号的this指的是访问该代码块的对象,从控制台上可以看出,访问代码块的对象指针地址是一样的,从而可以得出他们是固定同一对象。
分享到:
评论

相关推荐

    locks框架_ReentrantLock.pdf

    这份资源旨在详细讲解 Java 中的 Locks 框架,特别关注 ReentrantLock 的使用和原理。...通过这份资源,您将获得关于 Locks 框架和 ReentrantLock 的深入理解,从基本用法到高级功能,从对比分析到最佳实践。

    关于synchronized、Lock的深入理解

    众所周知,synchronized锁是JAVA的关键字,按理说是JAVA语言内置的特性,那为什么还要使用Lock呢 我们先说一说synchronized,当一个方法或者代码块被synchronized修饰,并执行到此方法或者代码块时,获取到锁并执行...

    locks框架:接口.pdf

    这份资源旨在介绍 Java Locks 框架中的 Lock 接口及其相关内容。Lock 接口是 Locks 框架的核心,提供...通过这份资源,您将获得关于 Locks 框架中 Lock 接口的深入理解,从基本用法到高级功能,从可重入性到条件变量。

    Java 多线程编程面试集锦20道问题解答Java多线程编程高难度面试题及解析

    您将了解线程安全的实现、死锁的避免策略、线程池的使用方法、线程上下文切换的原因与优化、线程同步与互斥的区别、volatile关键字的作用、synchronized关键字的用法等。同时,我们还探讨了多线程编程中 通过研究和...

    java并发编程:线程基础

    本资源致力于向您介绍 ...介绍如何使用 synchronized 关键字来实现线程的同步和阻塞。 线程间通信: 详解线程间通信的方法,包括 wait、notify 和 notifyAll 方法的使用。讲解如何通过这些方法实现线程的协作和同步。

    汪文君高并发编程实战视频资源下载.txt

    │ 高并发编程第一阶段19讲、结合jconsole,jstack以及汇编指令认识synchronized关键字.mp4 │ 高并发编程第一阶段20讲、同步代码块以及同步方法之间的区别和关系.mp4 │ 高并发编程第一阶段21讲、通过实验分析...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    如何使用方法的返回值? 166 7.4.4 使用return结束方法 166 7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    如何使用方法的返回值? 166 7.4.4 使用return结束方法 166 7.5 方法重载(overload):给汽车加速添个限制 168 7.5.1 什么是方法的签名 168 7.5.2 什么是重载?为什么要重载? 168 7.5.3 给汽车加个重载的方法...

Global site tag (gtag.js) - Google Analytics