`

【知识积累】java synchronized

 
阅读更多

    Java语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。其中同步块 (或方法)可以使用关键字synchronized或使用java.util.concurrent.lock 中的类 ReentrantLock。

这里我们只介绍synchronized,其他的将陆续的介绍。

   (1):为什么要采用synchronized等方式来同步。

        这主要和java内存模型(简称JMM),它分为主内存区(一般为堆)和工作内存区,java线程之间是不可见的,即工作内存(n个线程存在n个工作内存)是独立,相关的变量变化都是通过主内存(有且只有一个)体现,所有和变量都是存在主内存区,而线程使用变量时进行一下操作:

      1.从主内存中复制数据到工作内存
      2.执行代码,对数据进行各种操作和计算
      3.把操作后的变量值重新写回主内存中  

       当然这样的运行顺序也是我们所期望的!但是, JVM并不保证第1步和第3步会严格按照上述次序立即执行。因为根据java语言规范的规定,线程的工作内存和主存间的数据交换是松耦合的,什么时候需要刷新工作内存或者什么时候更新主存的内容,可以由具体的虚拟机实现自行决定。由于JVM可以对特征代码进行调优,也就改变了某些运行步骤的次序的颠倒,那么每次线程调用变量时是直接取自己的工作存储器中的值还是先从主存储器copy再取是没有保证的,任何一种情况都可能发生。同样的,线程改变变量的值之后,是否马上写回到主存储器上也是不可保证的,也许马上写,也许过一段时间再写。那么,在多线程的应用场景下就会出现问题了,多个线程同时访问同一个代码块,很有可能某个线程已经改变了某变量的值,当然现在的改变仅仅是局限于工作内存中的改变,此时JVM并不能保证将改变后的值立马写到主内存中去,也就意味着有可能其他线程不能立马得到改变后的值,依然在旧的变量上进行各种操作和运算,最终导致不可预料的结果。

       synchronized关键字强制实施一个互斥锁,使得被保护的代码块在同一时间只能有一个线程进入并执行。当然synchronized还有另外一个方面的作用:在线程进入synchronized块之前,会把工作存内存中的所有内容映射到主内存上,然后把工作内存清空再从主存储器上拷贝最新的值。而在线程退出synchronized块时,同样会把工作内存中的值映射到主内存,但此时并不会清空工作内存。这样一来就可以强制其按照上面的顺序运行,以保证线程在执行完代码块后,工作内存中的值和主内存中的值是一致的,保证了数据的一致性! 

    (2):synchronized使用方式

          2.1:synchronized 修饰方法。

          示例代码如下:

          

public synchronized void getVal(int Val); 

        Java 中,每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能

执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。 

注:在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。

 

class testSynchronized{
   private int Val;
   public synchronized void increaseVal( ){
       Val++;
   }
  public synchronized void decreaseVal( ){
       Val--;
   }
  
}

 

    2.2:通过 synchronized关键字来声明synchronized 块。语法如下:

synchronized(syncObject) { 
//允许访问控制的代码 
} 

 

    synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

    2.3:通过synchronized关键字修饰静态方法:
        static synchronized method(){}

    2.4:通过synchronized获取类锁:
        synchronized(classname.class)

注意:

       前面两个使用的锁是对象monitor,后面两者使用的是类monitor,都可以实现互斥访问。
一个对象只有一个对象monitor,一个类也只有一个类monitor。静态方法使用类monitor进行同步,而普通方法则使用对象monitor进行同步。

 (3)synchronized 块与方法访问具有以下规则:

一、当两个并发线程访问同一个对象object中的这个synchronized同步代码块或方法时时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、当一个线程访问object的一个synchronized同步代码块或方法时,另一个线程仍然可以访问该object中的非synchronized同步代码块或方法。

三、当一个线程访问object的一个synchronized同步代码块或方法时,它就获取到这个object的对象锁,其他线程对object中所有其它synchronized同步代码块或方法的访问将被阻塞。 

四、以上规则对其它对象锁同样适用 。

   锁是和对象相关联的,每个对象有一把锁,为了执行synchronized语句,线程必须能够获得synchronized语句中表达式指定的对象的锁,一个对象只有一把锁,被一个线程获得之后它就不再拥有这把锁,线程在执行完synchronized语句后,将获得锁交还给对象。

  在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法。同步化方法在执行之前获得一个锁。如果这是一个类方法,那么获得的锁是和声明方法的类相关的Class类对象的锁。如果这是一个实例方法,那么此锁是this对象的锁。

实例分析

package com.ailk.sms;

public class TestSynchronized  {
	int k=0;
	
	public static void main(String args[]){
		
		final TestSynchronized ts=new TestSynchronized();
		new Thread(){			
			public void run(){
				
				while( true ){
					System.out.println( "线程一");
					ts.operate(  );
				}
					//this.currentThread().sel

			}
		}.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread t2=new Thread(){
			
			public void run(){
				
				while( true  ){
					System.out.println( "线程二");
					ts.printK();
				}
			}
		};

		t2.start();
		
		new Thread(){
			
			public void run(){
				
				while( true  ){
					System.out.println( "线程三");
					ts.printK1();
				}
			}
		}.start();
		
	}
   
	synchronized public void operate( ) {

		System.out.println( "operate "+k );
		while( true ){
		//	this.k=k++;
			k=10000;
		}
	}
	
	synchronized	public void printK(){
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println( "printK "+k );
		
	}
	public void printK1(){
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println( "printK1 "+k );
		
	}
}

 执行结果为

线程一
operate 0
线程二
线程三
printK1 10000
线程三
printK1 10000
线程三
printK1 10000
线程三
printK1 10000

 分析:

       (1):该类TestSynchronized有三个函数,两个函数分别有synchronized修饰分别为operate,printK不含synchronized的普通函数。

        (2):main方法中含有三个线程分别调用TestSynchronized的三个函数。

        (3):执行结果为线程先执行线程1,线程1执行operate方法,维持类TestSynchronized对象的锁对象,但因为该方法是个死循环,因此线程1一直维持TestSynchronized对象的锁导致文件,线程二一直获取不到TestSynchronized对象锁一直阻塞【当两个并发线程访问同一个对象object中的这个synchronized同步代码块或方法时时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块】,而printK1普通的方法,不需要TestSynchronized对象的锁,所以正常输出【当一个线程访问object的一个synchronized同步代码块或方法时,另一个线程仍然可以访问该object中的非synchronized同步代码块或方法。】,

 

分享到:
评论

相关推荐

    java知识体系总结

    Java学习和掌握需要时间和实践,通过不断的学习和项目经验积累,才能成为一名熟练的Java开发者。上述知识点只是Java庞大知识体系的一部分,深入理解并熟练运用这些概念,对于成为一名优秀的Java程序员至关重要。

    java经验积累java经验积累

    本文将基于"java经验积累"这一主题,深入探讨Java的相关知识点,包括类加载器、并发处理、注解(Annotation)、线程池、泛型、自定义注解以及Java与模式思考。 首先,我们来关注"深入探讨 Java 类加载器"。Java的类...

    java知识点总结

    Java是一种广泛使用的面向对象的编程语言,以其平台独立性、高效性和安全性著称。这篇总结将涵盖Java基础知识,包括语法、类与对象、异常处理、集合...不断实践和项目经验积累,才能真正掌握Java编程并应用于实际场景。

    JAVA笔试题积累

    以下是一些基于标题"JAVA笔试题积累"和描述"JAVA笔试题积累"所涵盖的Java编程基础、核心概念和技术要点的详细说明: 1. **Java基础语法**:这是Java学习的基础,包括变量声明、数据类型(基本类型和引用类型)、...

    java面试资料积累,经验分享.zip

    这份"java面试资料积累,经验分享.zip"压缩包显然是一份宝贵的资源,包含了作者在面试过程中遇到的各类Java问题和经验总结,对于想要深入学习Java或者正在准备Java面试的人来说,无疑是宝贵的参考资料。 一、基础...

    多年Java精华积累

    以上只是Java编程中的一部分重要知识点,实际的“多年Java精华积累”文档可能还会涵盖更多细节,如设计模式、并发编程、JVM优化、数据库操作、网络编程等内容。通过系统学习和实践这些知识,可以成为一名优秀的Java...

    JAVA学习历程(基础知识个人总结1,2,3)

    这份"JAVA学习历程(基础知识个人总结1,2,3)"的文档集合,显然是一位热情的学习者在探索JAVA世界时,逐步积累并整理出的心得体会。这些文档可能包含了从入门到进阶的各类关键知识点,旨在帮助读者构建全面的JAVA...

    java考试知识

    以上只是Java知识体系的一部分,实际考试可能还会涉及反射、注解、模块系统、并发编程、垃圾回收机制、Lambda表达式、Stream API等更高级的主题。深入学习并理解这些知识点,将有助于你在Java考试中取得好成绩。

    java面经+知识点.zip

    线程的创建、同步机制(synchronized、Lock等)、线程池的使用、死锁的概念及避免策略都是需要掌握的知识点。 5. **IO/NIO/AIO**:输入输出流的使用、BufferedReader与FileReader的区别、NIO的非阻塞特性、AIO...

    Java开发岗面试知识点解析

    其是如果你的简历上写着你会 C++。答:1. Java 是完全面向对象的,而 C++ 支持面向过程...在面试过程中,不仅要掌握理论知识,还要注重实际问题解决能力和项目经验的积累,这样才能在竞争激烈的 IT 行业中脱颖而出。

    Java面试题积累

    以下是一些基于标题“Java面试题积累”和描述“Java面试题 初中级 判断 选择 编程”的关键知识点: 1. **基础语法**:初级面试题通常会涵盖Java的基础语法,如数据类型(基本类型与引用类型)、变量、运算符、流程...

    JAVA期末复习知识点整理

    针对"JAVA期末复习知识点整理"这个主题,我们可以深入探讨Java的核心概念、语法结构以及它在K12教育阶段的应用。 首先,让我们从基础开始。Java是一种面向对象的语言,它的基本语法包括变量、数据类型、运算符、...

    李兴华java笔记

    这份笔记集合了他在教学和实践中积累的丰富经验,旨在帮助学习者掌握Java的核心概念、设计模式以及实战技巧。通过这份笔记,读者可以系统地学习Java语言,并逐步提升为一名熟练的Java开发者。 笔记内容涵盖了以下几...

    java入门基础教程.zip

    Java入门基础教程是针对初学者精心编排的一套学习资源,旨在帮助新手快速掌握Java...随着实践经验的积累,可以进一步探索更高级的主题,如Java EE、Spring框架、并发编程、数据库连接等,从而成为熟练的Java开发者。

    Java面试宝典2013最新版

    14. Java中的同步机制,例如synchronized关键字的使用,以及wait()和sleep()方法的区别。 15. Java中的垃圾回收机制,以及对System.gc()方法的理解。 掌握这些知识点,对于通过Java相关的技术面试至关重要。面试者...

    java编程技术知识

    总之,Java编程技术的学习是一段旅程,从基础到高级,从理论到实践,不断探索和积累,你将逐渐成长为一名优秀的编程技术人员。在这个过程中,不断地学习、思考和动手实践,你将能够驾驭这个强大的工具,创造出更多有...

    java网络编程第四版pdf

    《Java网络编程(第四版)》是一本深入探讨Java在互联网环境下的编程技术的经典书籍。...通过阅读和实践书中的例子,读者不仅可以掌握理论知识,还能积累实际开发经验,从而在实际项目中更好地应用Java进行网络编程。

    java基础知识总结

    Java是一种广泛使用的面向对象的编程语言,以其跨平台、高...以上就是Java基础知识的概览,深入学习需要实践和项目经验的积累。通过“Study list”中的文件,你可以找到更详细的讲解和示例,进一步巩固和拓展这些概念。

    java教程(谭浩强)

    【Java教程(谭浩强)】是一本针对初学者的Java编程教材,由著名计算机教育专家谭浩强教授编写。本书以深入浅出的方式...在实践中不断积累经验,将理论知识转化为实际编程技巧,为后续的Java项目开发打下坚实的基础。

Global site tag (gtag.js) - Google Analytics