版权声明:版权归 爱装逼的文艺小青年所有 https://blog.csdn.net/toyota_rav4/article/details/84960812
ps:最近几天带媳妇回了趟家,博文没有更新。
目录
A线程持有同步方法的锁,B线程可以异步的调用该对象中的非同步方法
方法内部的私有变量为线程安全
在使用synchronized同步方法之前,我们先来做多个线程访问方法内部的私有变量,是否存在线程安全的问题。
package com.demo12;
public class MyObject {
public void addNum(String name){
try {
int num = 0;
if("a".equals(name)){
num = 100 ;
System.out.println("a 赋值完毕");
Thread.sleep(2000);
}else{
num = 200;
System.out.println("b 赋值完毕");
}
System.out.println("线程name=" + name + ";num=" + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.demo12;
public class ThreadA extends Thread {
private MyObject myObject;
public ThreadA(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
myObject.addNum("a");
}
}
package com.demo12;
public class ThreadB extends Thread{
private MyObject myObject;
public ThreadB(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
myObject.addNum("b");
}
}
package com.demo12;
public class Run {
public static void main(String[] args) {
MyObject myObject = new MyObject();
ThreadA threadA = new ThreadA(myObject);
ThreadB threadB = new ThreadB(myObject);
threadA.start();
threadB.start();
}
}
运行结果:
a 赋值完毕
线程name=a;num=100
b 赋值完毕
线程name=b;num=200
由输出结果可以看出:a线程赋值完毕之后,b线程才对num进行赋值。哪怕a sleep()了,b线程也没抢占cpu资源去执行赋值。方法内部的私有变量,不存在线程安全的问题。
实例变量非线程安全
package com.demo12;
public class MyObject {
private int num = 0;
public void addNum(String name){
try {
//int num = 0;
if("a".equals(name)){
num = 100 ;
System.out.println("a 赋值完毕");
Thread.sleep(2000);
}else{
num = 200;
System.out.println("b 赋值完毕");
}
System.out.println("线程name=" + name + ";num=" + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
将上例中的MyObject中的方法内部的变量改为实例变量,运行结果为:
a 赋值完毕
b 赋值完毕
线程name=b;num=200
(此处等待两秒)
线程name=a;num=200
线程a赋值完毕之后被sleep(),这时候b拿到了Cpu资源,对num进行了赋值,最后a才拿到cpu资源,继续走打印语句。但是num已经是被b给修改过了的。这就是非线程安全的场景。
给方法加锁
这个时候,我们可以使用synchronized对方法进行加锁,使上面的场景变成线程安全的!
package com.demo12;
public class MyObject {
private int num = 0;
synchronized public void addNum(String name){
try {
//int num = 0;
if("a".equals(name)){
num = 100 ;
System.out.println("a 赋值完毕");
Thread.sleep(2000);
}else{
num = 200;
System.out.println("b 赋值完毕");
}
System.out.println("线程name=" + name + ";num=" + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
a 赋值完毕
(此处等待了2秒)
线程name=a;num=100
b 赋值完毕
线程name=b;num=200
很明显为线程安全的了。
synchronized取得的锁是对象锁
修改上述代码如下:
package com.demo12;
public class MyObject {
private int num = 0;
synchronized public void addNum(String name){
try {
//int num = 0;
if("a".equals(name)){
num = 100 ;
System.out.println("a 赋值完毕");
Thread.sleep(2000);
}else{
num = 200;
System.out.println("b 赋值完毕");
}
System.out.println("线程name=" + name + ";num=" + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.demo12;
public class ThreadA extends Thread {
private MyObject myObject;
public ThreadA(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
myObject.addNum("a");
}
}
package com.demo12;
public class ThreadB extends Thread{
private MyObject myObject;
public ThreadB(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
myObject.addNum("b");
}
}
package com.demo12;
public class Run {
public static void main(String[] args) {
MyObject myObject1= new MyObject();
MyObject myObject2= new MyObject();
ThreadA threadA = new ThreadA(myObject1);
threadA.start();
ThreadB threadB = new ThreadB(myObject2);
threadB.start();
}
}
运行结果为:
a 赋值完毕
b 赋值完毕
线程name=b;num=200
(此处等待了2秒)
线程name=a;num=100
分析:虽然结果打印出来的形如异步,但是结果却没有乱。a , b 线程在获取CPU资源之后,分别持有synchronied方法所属的对象锁。如果不是分别拿到的两个对象的锁,最后的打印结果num=200。出现非线程安全的问题。
A线程持有同步方法的锁,B线程可以异步的调用该对象中的非同步方法
package com.demo13;
public class MyObject {
synchronized public void methodA(){
try {
System.out.println("同步methodA beigin , 线程名称=" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("同步methodA end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB(){
System.out.println("非同步methodB begin , 线程名称=" + Thread.currentThread().getName());
System.out.println("非同步methodB end");
}
}
package com.demo13;
public class ThreadA extends Thread {
private MyObject myObject;
public ThreadA(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
myObject.methodA();
}
}
package com.demo13;
public class ThreadB extends Thread {
private MyObject myObject;
public ThreadB(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run(){
myObject.methodB();
}
}
package com.demo13;
public class Run {
public static void main(String[] args) {
MyObject myObject = new MyObject();
ThreadA threadA = new ThreadA(myObject);
ThreadB threadB = new ThreadB(myObject);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
运行结果:
同步methodA beigin , 线程名称=A
非同步methodB begin , 线程名称=B
非同步methodB end
(此处等待5秒)
同步methodA end
脏读
上面所述的情况可能导致脏读
package com.demo14;
public class MyObject {
private String username = "1";
private String password = "11";
synchronized public void setValue(String username , String password){
try {
System.out.println("赋值线程名=" + Thread.currentThread().getName());
this.username = username;
Thread.sleep(2000);
this.password = password;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue(){
System.out.println("取值线程名=" + Thread.currentThread().getName());
System.out.println("username=" + username );
System.out.println("password=" + password);
}
}
package com.demo14;
public class ThreadA extends Thread {
MyObject myObject = new MyObject();
public ThreadA(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
myObject.setValue("2","22");
}
}
package com.demo14;
public class ThreadB extends Thread {
private MyObject myObject;
public ThreadB(MyObject myObject) {
this.myObject = myObject;
}
@Override
public void run() {
myObject.getValue();
}
}
package com.demo14;
public class Run {
public static void main(String[] args) {
try {
MyObject myObject = new MyObject();
ThreadA threadA = new ThreadA(myObject);
ThreadB threadB = new ThreadB(myObject);
threadA.setName("A");
threadB.setName("B");
threadA.start();
Thread.sleep(500); //给时间 保证让A将username赋值完毕
threadB.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
赋值线程名=A
取值线程名=B
username=2
password=11
很明显出现了脏读。修改MyObject.java文件
package com.demo14;
public class MyObject {
private String username = "1";
private String password = "11";
synchronized public void setValue(String username , String password){
try {
System.out.println("赋值线程名=" + Thread.currentThread().getName());
this.username = username;
Thread.sleep(2000);
this.password = password;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void getValue(){
System.out.println("取值线程名=" + Thread.currentThread().getName());
System.out.println("username=" + username );
System.out.println("password=" + password);
}
}
运行结果为:
赋值线程名=A
取值线程名=B
username=2
password=22
上述说明:B线程想要调用对象中没有被A线程锁住的同步方法B,必须等A线程释放A方法的同步锁。