线程同步方法

1  wait方法:

该方法属于Object的方法,wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行)。

调用wait方法需要注意几点:

第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。

第二点:恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况。

第三点:若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将被激活到竞争状态。

第四点:wait方法被调用的线程必须获得之前执行到wait时释放掉的锁重新获得才能够恢复执行。

2  notify方法和notifyAll方法:   

notify方法通知调用了wait方法,但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁。          notifyAll方法则唤醒所有调用了wait方法,尚未激活的进程进入竞争队列。

3 synchronized关键字:

第一点:synchronized用来标识一个普通方法时,表示一个线程要执行该方法,必须取得该方法所在的对象的锁。

第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须获得该方法所在的类的类锁。

第三点:synchronized修饰一个代码块。类似这样:synchronized(obj) { //code.... }。表示一个线程要执行该代码块,锁定当前对象。另外有线程想访问该对象时,它只能等着。

这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象做obj非常经济。

4 atomic action(原子操作)[定义:一旦调用就不会被其他线程被打断的操作]

在JAVA中,以下两点操作是原子操作。但是c和c++中并不如此。

第一点:对引用变量和除了long和double之外的原始数据类型变量进行读写。

第二点:对所有声明为volatile的变量(包括long和double)的读写。

【Volatile定义】要求编译器不要对其描述的对象作优化处理,对它的读写都需要从内存中访问。volatile在多线程中是用来同步变量的。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致的情况。

volatile就是用来避免这种情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A) 变量。

另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依赖于同步机制的线程安全的类和方法。

((

1:为什么会产生错误的数据?

多线程引起的,因为对于多线程同时操作一个整型变量在大并发操作的情况下无法做到同步,而Atom提供了很多针对此类线程安全问题的解决方案,因此解决了同时读写操作的问题。

2:为什么会造成同步问题?

Java多线程在对变量进行操作的时候,实际上是每个线程会单独分配一个针对i值的拷贝(独立内存区域),但是申明的i值确是在主内存区域中,当对i值修改完毕后,线程会将自己内存区域块中的i值拷贝到主内存区域中,因此有可能每个线程拿到的i值是不一样的,从而出现了同步问题。

3:为什么使用volatile修饰integer变量后,还是不行?

因为volatile仅仅只是解决了存储的问题,即i值只是保留在了一个内存区域中,但是i++这个操作,涉及到获取i值、修改i值、存储i值(i=i+1),这里的volatile只是解决了存储i值得问题,至于获取和修改i值,确是没有做到同步。

4:既然不能做到同步,那为什么还要用volatile这种修饰符?

主要的一个原因是方便,因为只需添加一个修饰符即可,而无需做对象加锁、解锁这么麻烦的操作。但是本人不推荐使用这种机制,因为比较容易出问题(脏数据),而且也保证不了同步。

5:那到底如何解决这样的问题?

第一种:采用同步synchronized解决,这样虽然解决了问题,但是也降低了系统的性能。

第二种:采用原子性数据Atomic变量,这是从JDK1.5开始才存在的针对原子性的解决方案,这种方案也是目前比较好的解决方案了。

6:Atomic的实现基本原理?

首先Atomic中的变量是申明为了volatile变量的,这样就保证的变量的存储和读取是一致的,都是来自同一个内存块,然后Atomic提供了getAndIncrement方法,该方法对变量的++操作进行了封装,并提供了compareAndSet方法,来完成对单个变量的加锁和解锁操作,方法中用到了一个UnSafe的对象,现在还不知道这个UnSafe的工作原理(似乎没有公开源代码)。Atomic虽然解决了同步的问题,但是性能上面还是会有所损失,不过影响不大,网上有针对这方面的测试,大概50million的操作对比是250ms : 850ms,对于大部分的高性能应用,应该还是够的了。

Atomic提供了getAndIncrement方法,封装了++))

5.Lock

读锁可以允许多个进行读操作的线程同时进入,但不允许写进程进入;写锁只允许一个写进程进入,在这期间任何进程都不能再进入。

类似“面向过程”,在方法中需要锁的时候lock,在结束的时候unlock(一般都在finally块里)。

如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断

如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

6.Hibernate 悲观锁 乐观锁

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。

悲观锁的应用:如果并发量不大且不允许脏读

String hqlStr ="from TUser as user where user.name=‘Erica‘";

Query query = session.createQuery(hqlStr);

query.setLockMode("user",LockMode.UPGRADE); // 加锁

List userList = query.list();// 执行查询,获取数据

query.setLockMode 对查询语句中,特定别名所对应的记录进行加锁(我们为 TUser 类指定了一个别名 “user” ),这里也就是对

Hibernate 的加锁模式有:

Ø LockMode.NONE : 无锁机制。

Ø LockMode.WRITE : Hibernate 在 Insert 和 Update 记录的时候会自动获取

Ø LockMode.READ : Hibernate 在读取记录的时候会自动获取。

以上这三种锁机制一般由 Hibernate 内部使用,如 Hibernate 为了保证 Update

过程中对象不会被外界修改,会在 save 方法实现中自动为目标对象加上 WRITE 锁。

Ø LockMode.UPGRADE :利用数据库的 for update 子句加锁。

上面这两种锁机制是我们在应用层较为常用的,加锁一般通过以下方法实现:

Criteria.setLockMode

Query.setLockMode

Session.lock

注意,只有在查询开始之前(也就是 Hiberate 生成 SQL 之前)设定加锁,才会

真正通过数据库的锁机制进行加锁处理,否则,数据已经通过不包含 for update

子句的 Select SQL 加载进来,所谓数据库加锁也就无从谈起。

乐观锁应用

1.      使用自增长的整数表示数据版本号。更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6+1,使用该version值(=7)与数据库version+1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误。

2.      使用时间戳来实现.

注:对于以上两种方式,Hibernate自带实现方式:在使用乐观锁的字段前加annotation: @Version, Hibernate在更新时自动校验该字段。

<class name="User"  table="user" optimistic-lock="version" >

<id name="id">

<generator class="native" />

</id>

<!--version标签必须跟在id标签后面-->

<version column="version" name="version"  />

<property name="userName"/>

<property name="password"/>

</class>

时间: 2024-10-15 02:16:50

线程同步方法的相关文章

多线程编程——线程同步方法

1.五种方式 1.1 synchronized同步方法 使用synchronized关键字修饰的方法.java每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法.在调用该方法前,需获取内置锁,否则就会处于阻塞状态. 如:public synchronized void save(){} 注:当synchronized关键字修饰静态方法时,会锁住整个类 1.2 synchronized同步代码块 即有synchronized关键字修饰的语句块.被该关键字修饰的语句块会自动被加上内

C#线程同步方法汇总

我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在 后台处理一大堆数据,但还要使用户界面处于可操作状态:或者你的程序需要访问一些外部资源如数据库或网络文件等.这些情况你都可以创建一个子线程去处理, 然而,多线程不可避免地会带来一个问题,就是线程同步的问题.如果这个问题处理不好,我们就会得到一些非预期的结果. 在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳. 一.volatile关键字 volatile是最简单的一种同步方法,当然简单是要付出代

java线程同步方法,方法块区别

先说同步方法,它到底是锁定的当前对象,还是当前类 代码块1 package com.ssss; public class Thread1 implements Runnable { //public static Object o=new Object(); public void run() { pt(); } public synchronized void pt(){ int a=0; //synchronized(o) { for (int i = 0; i < 5; i++) { a+

synchronized:线程同步方法使用总结

synchronized: 1.多线程执行同一对象的synchronized函数,线程同步正确: 实例代码如下: public class Test1 implements Runnable{ //定义同一对象 static Test2 action; public static void main(String[] args) { // TODO Auto-generated method stub Class cls; try { cls = Class.forName("Test2&quo

C#线程同步方法——Monitor

Monitor类TryEnter()方法需要注意的地方: 由于Monitor.TryEnter()执行本身也会获取锁对象(在返回true的情况下),所以根据“如果调用线程调用 Exit 与调用 Enter 的次数不同,则该锁不会被释放”的原则,如果不针对Monitor.TryEnter()返回ture的情况调用一次Monitor.Exit()方法,会使得其他线程无法获取锁对象.

java线程详细介绍

目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1

Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会引起此共享资源的不一致性.因此,为避免线程安全问题,应该避免多线程环境下对此共享资源的并发访问. 线程安全问题多是由全局变量和静态变量引起的,当多个线程对共享数据只执行读操作,不执行写操作时,一般是线程安全的:当多个线程都执行写操作时,需要考虑线程同步来解决线程安全问题. 二.线程同步(synchr

C#编程总结(三)线程同步

C#编程总结(三)线程同步 在应用程序中使用多个线程的一个好处是每个线程都可以异步执行.对于 Windows 应用程序,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应.对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力.否则,在完全满足前一个请求之前,将无法处理每个新请求.然而,线程的异步特性意味着必须协调对资源(如文件句柄.网络连接和内存)的访问.否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作. "如果觉得有用,请帮顶! 如果有

(转)java 线程

转自 http://blog.csdn.net/column/details/java-thread.html http://www.ibm.com/developerworks/cn/java/j-concurrent/ synchronized 的语义确实保证了一次只有一个线程可以访问被保护的区段,但同时还包括同步线程在主存内互相作用的规则.理解 Java 内存模型(JMM)的一个好方法就是把各个线程想像成运行在相互分离的处理器上,所有的处理器存取同一块主存空间,每个处理器有自己的缓存,但这