Java多线程-线程的同步与锁【转】
出处:http://www.cnblogs.com/linjiqin/p/3208843.html
一、同步问题提出
线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。 例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。
例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。
package cn.thread;
public class Foo {
private int x = 100;
public int getX() {
return x;
}
public int fix(int y) {
x = x - y;
return x;
}
}
package cn.thread;
public class MyRunnable implements Runnable {
private Foo foo = new Foo();
public static void main(String[] args) {
MyRunnable run = new MyRunnable();
Thread ta = new Thread(run, "Thread-A");
Thread tb = new Thread(run, "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-B : 当前foo对象的x值= 40
Thread-A : 当前foo对象的x值= 40
Thread-B : 当前foo对象的x值= -20
Thread-A : 当前foo对象的x值= -20
Thread-B : 当前foo对象的x值= -80
Thread-A : 当前foo对象的x值= -80
从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。
如果要保持结果的合理性,只需要达到一个目的,就是将对Foo的访问加以限制,每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。
在具体的Java代码中需要完成一下两个操作: 把竞争访问的资源类Foo变量x标识为private; 同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。
package cn.thread;
public class Foo2 {
private int x = 100;
public int getX() {
return x;
}
//同步方法
public synchronized int fix(int y) {
x = x - y;
System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" + y
+ "”,当前值为:" + x);
return x;
}
// //同步代码块
// public int fix(int y) {
// synchronized (this) {
// x = x - y;
// System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" + y
// + "”,当前值为:" + x);
// }
//
// return x;
// }
}
package cn.thread;
public class MyRunnable2 {
public static void main(String[] args) {
MyRunnable2 run = new MyRunnable2();
Foo2 foo2=new Foo2();
MyThread t1 = run.new MyThread("线程A", foo2, 10);
MyThread t2 = run.new MyThread("线程B", foo2, -2);
MyThread t3 = run.new MyThread("线程C", foo2, -3);
MyThread t4 = run.new MyThread("线程D", foo2, 5);
t1.start();
t2.start();
t3.start();
t4.start();
}
class MyThread extends Thread {
private Foo2 foo2;
/**当前值*/
private int y = 0;
MyThread(String name, Foo2 foo2, int y) {
super(name);
this.foo2 = foo2;
this.y = y;
}
public void run() {
foo2.fix(y);
}
}
}
线程线程A运行结束,减少“10”,当前值为:90
线程线程C运行结束,减少“-3”,当前值为:93
线程线程B运行结束,减少“-2”,当前值为:95
线程线程D运行结束,减少“5”,当前值为:90
二、同步和锁定
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考试范围了。
六、线程安全类
当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。
即使是线程安全类,也应该特别小心,因为操作的线程是间仍然不一定安全。
七、线程同步小结
1、线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏。 2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。 3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。 4、对于同步,要时刻清醒在哪个对象上同步,这是关键。 5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。 6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。 7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
Java多线程-线程的同步与锁【转】的更多相关文章
- Java多线程-线程的同步与锁
一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏.例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. package ...
- Java多线程-线程的同步(同步方法)
线程的同步是保证多线程安全访问竞争资源的一种手段.线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些 ...
- Java多线程-线程的同步(同步代码块)
对于同步,除了同步方法外,还可以使用同步代码块,有时候同步代码块会带来比同步方法更好的效果. 追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问 ...
- Java多线程 - 线程同步
多线程操作同一个对象时,容易引发线程安全问题.为了解决线程安全问题,Java多线程引入了同步监视器. 同步代码块 同步代码块语法格式如下: synchronized(obj){ //此处的代码即为同步 ...
- Java多线程——线程之间的同步
Java多线程——线程之间的同步 摘要:本文主要学习多线程之间是如何同步的,如何使用volatile关键字,如何使用synchronized修饰的同步代码块和同步方法解决线程安全问题. 部分内容来自以 ...
- Java多线程——线程八锁案例分析
Java多线程——线程八锁案例分析 摘要:本文主要学习了多线程并发中的一些案例. 部分内容来自以下博客: https://blog.csdn.net/dyt443733328/article/deta ...
- (转)Java线程:线程的同步与锁
Java线程:线程的同步与锁 一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Fo ...
- Java多线程—线程同步(单信号量互斥)
JDK中Thread.State类的几种状态 线程的生命周期 线程的安全问题(同步与互斥) 方法一:同步代码块 多个线程的同步监视器(锁)必须的是同一把,任何一个类的对象都可以 syn ...
- java 多线程—— 线程让步
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
随机推荐
- Oracle中job的实例
一.Oracle定时器(Job)各时间段写法汇总 对于DBA来说,数据库Job再熟悉不过了,因为经常要数据库定时的自动执行一些脚本,或做数据库备份,或做数据的提炼,或做数据库的性能优化,包括重建索引等 ...
- EBS 信用检查(一)
信用逻辑 This post will more focus on Technical part of credit check Functionality. You can check the fu ...
- Dynamics CRM 通过OData查询数据URI中包含中文的情况
filter条件如下"?$filter=new_name eq '采购主管' and new_entityname eq 'new_purchaseenquiry' ",如果用这个 ...
- Linux共享内存编程实例
/*共享内存允许两个或多个进程进程共享同一块内存(这块内存会映射到各个进程自己独立的地址空间) 从而使得这些进程可以相互通信. 在GNU/Linux中所有的进程都有唯一的虚拟地址空间,而共享内存应用编 ...
- Oracle Applications DBA 基础(一)
1.引子 2014年9月13日 20:33 <oracle Applications DBA 基础>介绍Oracle Applications R12的系统架构, 数据库后台及应用系统的基 ...
- ASI与AFN网络请求的的比较
对比 ASI AFN 更新状态 2012年10月份,已经停止更新 持续更新中,目前已更新至3.0版 介绍 ASI的直接操作对象ASIHTTPRequest,是一个实现了了NSCopying协议的NSO ...
- git使用中checkout生成临时br的问题(吓出一身冷汗啊)
git中几天前漫不经心的使用了git checkout ver_hash的命令,结果push到远程库都提示everything is up-to-date,实际神马都没提交上去啊!但看本地log中的确 ...
- ruby通过telnet读取互联网时间
在前面的博文ntp服务器也谈逆向工程中,本猫曾经武断的认为telnet是无法连接到ntp服务器的.因为当时是这样连接的: telnet time.nist.gov 123,端口号123是在/etc/s ...
- day07_Tomcat服务器与http学习笔记
============================================================ 一.Tomcat服务器(很熟悉) 1.Web开发概述 WEB,在英语中web即 ...
- seo建站需要注意哪些
seo建站是搜索引擎优化的意思,通过seo建站技术,我们可以为站点带来可观的流量.那么,要怎样才能做好seo建站?seo建站优化过程中应该注意的事项有哪些?这些问题,是每一个站长都关心的.鉴于此,笔者 ...