title: 对象及变量的并发访问
categories: Java多线程编程核心技术
tags: 多线程
time: 2019-04-21 08:36:04
synchronized同步方法
“非线程安全”会在多个线程中的同一对象的实例变量在并发访问时发生,产生的后果就是“脏读”,即读到的数据其实是被更改过的。
“线程安全”就是以获得的实例变量的值是经过同步处理过的,不会出现“脏读”现象。
方法内的变量为线程安全
“非线程安全”问题存在与“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,即线程安全。
实例变量非线程安全
如果多个线程访问一个对象的实例变量,则有可能产生“非线程安全”问题。
public class HasSelfPrivateNum {
private int num = 0;
public void addNum(String name) {
try {
if ("a".equals(name)) {
num = 100;
System.out.println("a set over");
Thread.sleep(200);
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(name + " num=" + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
Test1 test1 = new Test1(hasSelfPrivateNum);
Test2 test2 = new Test2(hasSelfPrivateNum);
test1.start();
test2.start();
}
}
class Test1 extends Thread {
private HasSelfPrivateNum hasSelfPrivateNum;
Test1(HasSelfPrivateNum hasSelfPrivateNum) {
this.hasSelfPrivateNum = hasSelfPrivateNum;
}
@Override
public void run() {
super.run();
hasSelfPrivateNum.addNum("a");
}
}
class Test2 extends Thread {
private HasSelfPrivateNum hasSelfPrivateNum;
Test2(HasSelfPrivateNum hasSelfPrivateNum) {
this.hasSelfPrivateNum = hasSelfPrivateNum;
}
@Override
public void run() {
super.run();
hasSelfPrivateNum.addNum("b");
}
}
运行结果:
a set over
b set over
b num=200
a num=200
分析:Test1和Test2两个线程同时访问一个没有同步的方法addNum,则出现了线程不安全的问题。解决方式只需要给addNum方法前加synchronized关键字。
加锁后的执行效果:
a set over
a num=100
b set over
b num=200
多个对象多个锁
public static void main(String[] args) {
HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
HasSelfPrivateNum hasSelfPrivateNum2 = new HasSelfPrivateNum();
Test1 test1 = new Test1(hasSelfPrivateNum);
Test2 test2 = new Test2(hasSelfPrivateNum2);
test1.start();
test2.start();
}
运行结果:
a set over
b set over
b num=200
a num=100
分析:两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是异步的方式运行的。
关键字synchronized取得的锁是对象锁,而不是把一段代码或方法作为锁,所以在上面的示例中哪个线程先执行带synchrozined关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。但如果多个线程访问多个对象,例如上面示例中创建了2个HasSelfPrivateNum.java对象,所以就会产生2个锁,会异步执行。
synchronized方法与锁对象
验证synchronized与对象的关系
public class SynchronizedMethodLock {
public void methodA() {
try {
System.out.println(Thread.currentThread().getName() + " run");
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedMethodLock obj = new SynchronizedMethodLock();
ThreadA threadA = new ThreadA(obj);
threadA.setName("A");
ThreadB threadB = new ThreadB(obj);
threadB.setName("B");
threadA.start();
threadB.start();
}
}
class ThreadA extends Thread {
private SynchronizedMethodLock lock;
public ThreadA(SynchronizedMethodLock obj) {
this.lock = obj;
}
@Override
public void run() {
super.run();
lock.methodA();
}
}
class ThreadB extends Thread {
private SynchronizedMethodLock lock;
public ThreadB(SynchronizedMethodLock obj) {
this.lock = obj;
}
@Override
public void run() {
super.run();
lock.methodA();
}
}
运行结果:
A run
B run
end
end
分析:给methodA不加锁的情况下,是异步执行的;
synchronized public void methodA() {
try {
System.out.println(Thread.currentThread().getName() + " run");
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
给methodA加锁后是同步执行的,即排队执行。
A run
end
B run
end
结论:
1.调用synchronized声明的方法是排队运行的;
2.共享资源的读写需要同步化,否则就没有同步的必要。
同步方法和普通方法执行验证
public class SynchronizedMethodLock2 {
synchronized public void methodA() {
try {
System.out.println(Thread.currentThread().getName() + " run:" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB() {
try {
System.out.println(Thread.currentThread().getName() + " run:" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SynchronizedMethodLock2 obj = new SynchronizedMethodLock2();
ThreadC threadC = new ThreadC(obj);
threadC.setName("C");
ThreadD threadD = new ThreadD(obj);
threadD.setName("D");
threadC.start();
threadD.start();
}
}
class ThreadC extends Thread {
private SynchronizedMethodLock2 lock;
public ThreadC(SynchronizedMethodLock2 obj) {
this.lock = obj;
}
@Override
public void run() {
super.run();
lock.methodA();
}
}
class ThreadD extends Thread {
private SynchronizedMethodLock2 lock;
public ThreadD(SynchronizedMethodLock2 obj) {
this.lock = obj;
}
@Override
public void ru