多线程:synchronized同步方法
2018-03-04 01:09
369 查看
两个线程操作同一个对象里的实例变量,为什么是实例变量?因为局部变量是没有线程安全问题的。
代码如下:public class HasSelfPrivateNum {
private int num = 0;
public void addi(String username){ (1)
try{
if (username.equals("a")){
num = 100;
System.out.println("a set over!");
Thread.sleep(3000);
}else {
num = 200;
System.out.println("b set over!");
}
System.out.println( username + " num = " + num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
private HasSelfPrivateNum num;
public ThreadA(HasSelfPrivateNum num){
this.num = num;
}
@Override
public void run() {
super.run();
num.addi("a");
}
}
public class ThreadB extends Thread{
private HasSelfPrivateNum num;
public ThreadB(HasSelfPrivateNum num){
this.num = num;
}
@Override
public void run() {
super.run();
num.addi("b");
}
}
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum num = new HasSelfPrivateNum();
// HasSelfPrivateNum num1 = new HasSelfPrivateNum(); (2)
ThreadA threadA = new ThreadA(num);
threadA.start();
ThreadB threadB = new ThreadB(num); (3)
threadB.start();
}
}执行结果:a set over!
b set over!
b num = 200
a num = 200执行结果显然发生了线程安全的问题。
使用synchronized同步方法:
在HasSelfPrivateNum的方法(1)上添加sychronized,即synchronized public void addi(String username){...}此时的执行结果为:a set over!
a num = 100
b set over!
b num = 200继续,在之前添加synchronized的基础上,我们将之前两个线程访问同一个对象改为每个线程单独访问一个对象。
将Run类中的(1)的注释打开,将(3)处传入的对象改为num1。
此时的执行结果为:a set over!
b set over!
b num = 200
a num = 100可以看到没有线程安全问题,但是执行结果的顺序是交叉的。
这是因为关键词synchronized取得的都是对象的锁,而不是把一段代码或方法当做锁。所以当两个线程访问同一个对象的时候,这个对象的锁没有释放另一个线程就无法访问,执行结果就会是按照顺序的。
但是如果两个线程执行的是同一个类的两个对象,那么就会创建两个锁,两个线程分别执行互不影响。所以执行结果就会是交叉的。
脏读
代码如下:public class PublicVar {
public String username = "A";
public String password = "AA";
synchronized public void setValue(String username,String password){
try {
this.username = username;
Thread.sleep(1000);
this.password = password;
System.out.println("current thread = " + Thread.currentThread().getName() + " username = " + username
+ " password = " + password);
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void getValue(){
System.out.println("username = " + username + " password = " + password);
}
}
public class ThreadA extends Thread {
private PublicVar publicVar;
public ThreadA(PublicVar publicVar){
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B","BB");
}
}
public class Test
{
public static void main(String[] args) {
try {
PublicVar publicVar = new PublicVar();
ThreadA threadA = new ThreadA(publicVar);
threadA.start();
Thread.sleep(500); // 打印结果受此值影响,大于线程threadA(即setValue方法)休眠的时间就不会出现脏读
publicVar.getValue();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}执行结果:username = B password = AA
current thread = Thread-0 username = B password = BB如果也将getValue设为sychronized,那么执行结果:current thread = Thread-0 username = B password = BB
username = B password = BB此实验可以得到另个结论:
1,A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object的非sychronized;
2,A线程持持有object对象的Lock锁,B线程如果要调用object的sychronized类型方法则需等待,也就是同步。
第一次执行的时候,线程threadA先获得publicVar对象的锁,但是main线程依然可以调用publicVar对象的非sychronized方法getValue,此时username已被更改,password没被该。
第二次执行的时候,线程threadA先获得publicVar对象的锁,但是main线程在threadA没有执行完成setValue方法之前是不可以调用publicVar对象的sychronized方法getValue的,也就是只有threadA释放了锁,将username和password都赋值了,main线程才可以获取publicVar的锁进而调用getValue方法。
sychronized锁重入
sychronized关键字拥有锁重入的功能,也就是在一个线程得到一个对象琐时,再次请求此对象锁时是可以得到对象锁的,这也证明了在sychronized方法内部调用本类的其他sychronized方法时,是可以永远得到锁的。
锁异常自动释放
当一个线程持执行的代码出现异常时,其所持有的锁会自动释放。
代码如下:public class HasSelfPrivateNum {
private int num = 0;
public void addi(String username){ (1)
try{
if (username.equals("a")){
num = 100;
System.out.println("a set over!");
Thread.sleep(3000);
}else {
num = 200;
System.out.println("b set over!");
}
System.out.println( username + " num = " + num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
private HasSelfPrivateNum num;
public ThreadA(HasSelfPrivateNum num){
this.num = num;
}
@Override
public void run() {
super.run();
num.addi("a");
}
}
public class ThreadB extends Thread{
private HasSelfPrivateNum num;
public ThreadB(HasSelfPrivateNum num){
this.num = num;
}
@Override
public void run() {
super.run();
num.addi("b");
}
}
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum num = new HasSelfPrivateNum();
// HasSelfPrivateNum num1 = new HasSelfPrivateNum(); (2)
ThreadA threadA = new ThreadA(num);
threadA.start();
ThreadB threadB = new ThreadB(num); (3)
threadB.start();
}
}执行结果:a set over!
b set over!
b num = 200
a num = 200执行结果显然发生了线程安全的问题。
使用synchronized同步方法:
在HasSelfPrivateNum的方法(1)上添加sychronized,即synchronized public void addi(String username){...}此时的执行结果为:a set over!
a num = 100
b set over!
b num = 200继续,在之前添加synchronized的基础上,我们将之前两个线程访问同一个对象改为每个线程单独访问一个对象。
将Run类中的(1)的注释打开,将(3)处传入的对象改为num1。
此时的执行结果为:a set over!
b set over!
b num = 200
a num = 100可以看到没有线程安全问题,但是执行结果的顺序是交叉的。
这是因为关键词synchronized取得的都是对象的锁,而不是把一段代码或方法当做锁。所以当两个线程访问同一个对象的时候,这个对象的锁没有释放另一个线程就无法访问,执行结果就会是按照顺序的。
但是如果两个线程执行的是同一个类的两个对象,那么就会创建两个锁,两个线程分别执行互不影响。所以执行结果就会是交叉的。
脏读
代码如下:public class PublicVar {
public String username = "A";
public String password = "AA";
synchronized public void setValue(String username,String password){
try {
this.username = username;
Thread.sleep(1000);
this.password = password;
System.out.println("current thread = " + Thread.currentThread().getName() + " username = " + username
+ " password = " + password);
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void getValue(){
System.out.println("username = " + username + " password = " + password);
}
}
public class ThreadA extends Thread {
private PublicVar publicVar;
public ThreadA(PublicVar publicVar){
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B","BB");
}
}
public class Test
{
public static void main(String[] args) {
try {
PublicVar publicVar = new PublicVar();
ThreadA threadA = new ThreadA(publicVar);
threadA.start();
Thread.sleep(500); // 打印结果受此值影响,大于线程threadA(即setValue方法)休眠的时间就不会出现脏读
publicVar.getValue();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}执行结果:username = B password = AA
current thread = Thread-0 username = B password = BB如果也将getValue设为sychronized,那么执行结果:current thread = Thread-0 username = B password = BB
username = B password = BB此实验可以得到另个结论:
1,A线程先持有object对象的Lock锁,B线程可以以异步的方式调用object的非sychronized;
2,A线程持持有object对象的Lock锁,B线程如果要调用object的sychronized类型方法则需等待,也就是同步。
第一次执行的时候,线程threadA先获得publicVar对象的锁,但是main线程依然可以调用publicVar对象的非sychronized方法getValue,此时username已被更改,password没被该。
第二次执行的时候,线程threadA先获得publicVar对象的锁,但是main线程在threadA没有执行完成setValue方法之前是不可以调用publicVar对象的sychronized方法getValue的,也就是只有threadA释放了锁,将username和password都赋值了,main线程才可以获取publicVar的锁进而调用getValue方法。
sychronized锁重入
sychronized关键字拥有锁重入的功能,也就是在一个线程得到一个对象琐时,再次请求此对象锁时是可以得到对象锁的,这也证明了在sychronized方法内部调用本类的其他sychronized方法时,是可以永远得到锁的。
锁异常自动释放
当一个线程持执行的代码出现异常时,其所持有的锁会自动释放。
相关文章推荐
- Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)
- 【多线程】synchronized同步方法
- 初学Java多线程:使用Synchronized关键字同步类方法
- 初学Java多线程:使用Synchronized关键字同步类方法
- 多线程--synchronized同步方法
- java多线程之-----静态同步synchronized方法与synchronized(class) 代码块
- 九、初学Java多线程:使用Synchronized关键字同步类方法
- java多线程编程之使用Synchronized关键字同步类方法
- 初学Java多线程:使用Synchronized关键字同步类方法
- (转)初学Java多线程:使用Synchronized关键字同步类方法
- 从头认识多线程-2.25 synchronized同步方法在jvm是怎样执行的?
- 多线程中的同步之synchronized应用——synchronized方法
- java 多线程10:synchronized锁机制 之 锁定类静态方法 和锁定类.Class 和 数据String的常量池特性 同步静态方法
- 多线程编程-Synchronized同步方法(二)
- Java多线程之同步锁定--synchronized同步方法和同步块、脏读、锁重入、死琐
- day11多线程,run方法。卖票多线程示例。synchronized,同步锁对象,回顾单例。死锁问题
- (转)初学Java多线程:使用Synchronized关键字同步类方法
- 多线程数据同步之synchronized同步方法-多线程访问临界资源
- JavaSE8基础 多线程synchronized 同步的成员方法用的锁对象是this
- 从头认识多线程-2.14 解决由同步的synchronized (newobject()) 引起的脏读的方法