对象及变量的并发访问
1 synchronized同步方法
1.3 方法内部的变量都是不存在线程安全问题,永远都是线程安全的,因为方法内部的变量是私有的特性造成的。
1.4 实例变量就会存在线程安全问题
package thread;
public class HaselfPrivateNum {
private int num = 0 ;
public void addI(String username) throws InterruptedException {
if(username.equals("a")) {
num = 100;
System.out.println("a set over");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(username + " num = " +num);
}
}
package thread;
public class ThreadA extends Thread {
private HaselfPrivateNum numref;
public ThreadA(HaselfPrivateNum numref) {
super();
this.numref = numref;
}
@Override
public void run() {
super.run();
try {
numref.addI("a");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package thread;
public class ThreadB extends Thread {
private HaselfPrivateNum numref;
public ThreadB(HaselfPrivateNum numref) {
super();
this.numref = numref;
}
@Override
public void run() {
super.run();
try {
numref.addI("b");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package thread;
public class Run {
public static void main(String[] args) {
HaselfPrivateNum haselfPrivateNum = new HaselfPrivateNum();
ThreadA A = new ThreadA(haselfPrivateNum);
A.start();
ThreadB B = new ThreadB(haselfPrivateNum);
B.start();
}
}
结果展示
a set over
b set over
b num = 200
a num = 200
添加一个sycnhroized()同步方法
package thread;
public class HaselfPrivateNum {
private int num = 0 ;
synchronized public void addI(String username) throws InterruptedException {
if(username.equals("a")) {
num = 100;
System.out.println("a set over");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(username + " num = " +num);
}
}
主方法
package thread;
public class Run {
public static void main(String[] args) {
HaselfPrivateNum haselfPrivateNum = new HaselfPrivateNum();
ThreadA A = new ThreadA(haselfPrivateNum);
A.start();
ThreadB B = new ThreadB(haselfPrivateNum);
B.start();
}
}
结果展示:
b set over
b num = 200
a set over
a num = 100
关键字synchronized取得的锁都是对象锁,而不是把一小段代码方法当做锁,所以线程先执行那个带synchronized关键字的方法,就先获得哪一个对象锁,由于new 了两个实例对象,所以有两个锁。只有共享资源的读写访问才需要同步化。aynchronized是异步锁。
1.5 脏读,synchronized可重入锁,异常线程锁自动释放,同步不具有继承性
发生脏读的情况,就是取值时,实例变量已经被其他线程修改了,加上synchronized方法就可以解决.就是当第一次调用对象锁时,在其内部在次调用,是可以再次获取锁的,虽然第一次的锁没有解开。不然会造成死锁问题
1.6同步代码块
同步代码块,当一个线程正在访问一个Object中的同步代码块时候,另外一个线程可以访问Object中非同步的代码块,可以一半异步,一半同步·,当线程访问一个Object同步代码块时,将停止访问这个object类中的另外一个同步代码块
package threadMainTest;
public class ObjectService {
public void serviceMethodA() {
synchronized(this) {
System.out.println("A start time is " + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("A end time is " + System.currentTimeMillis());
}
}
public void serviceMethodB() {
synchronized(this) {
System.out.println("B start time is " + System.currentTimeMillis());
System.out.println("B end time is " + System.currentTimeMillis());
}
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadA extends Thread {
private ObjectService objectService;
public ThreadA(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
objectService.serviceMethodA();
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadB extends Thread {
private ObjectService objectService;
public ThreadB(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
objectService.serviceMethodB();
}
}
package threadMainTest;
public class Run2 {
public static void main(String[] args) throws InterruptedException {
ObjectService objectService = new ObjectService();
ThreadA threadA = new ThreadA(objectService);
threadA.start();
//Thread.sleep(200);
ThreadB threadB = new ThreadB(objectService);
threadB.start();
}
}
运行结果:
A start time is 1543307979305
A end time is 1543307981313
B start time is 1543307981313
B end time is 1543307981313
注:当多个线程调用同一个对象中的Synchroinzed同步方法或Synchroized(this)同步代码块的时,调用的效果是安顺序执行,也就是同步的,阻塞的。
1.7 静态同步Synchronized
当synchronized加到Static方法上,是给Class类上锁,加到非Static方法上,是给对象上锁。String常量池对Synchronized的影响。
package threadMainTest;
public class ObjectService {
public static void print(String username) {
synchronized (username) {
while(true) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadA extends Thread {
private ObjectService objectService;
public ThreadA(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
objectService.print("AA");
}
}
package threadMainTest;
import org.omg.PortableInterceptor.ObjectReferenceTemplateSeqHolder;
public class ThreadB extends Thread {
private ObjectService objectService;
public ThreadB(ObjectService objectService) {
super();
this.objectService = objectService;
}
@Override
public void run() {
objectService.print("AA");
}
}
package threadMainTest;
public class Run2 {
public static void main(String[] args) throws InterruptedException {
ObjectService objectService = new ObjectService();
ThreadA threadA = new ThreadA(objectService);
threadA.setName("A");
threadA.start();
//Thread.sleep(200);
ThreadB threadB = new ThreadB(objectService);
threadB.setName("B");
threadB.start();
}
}
结果展示
A
A
A
A
A
A
A
A
A
A
A
因为两个线程都是里面的输入字符都是AA,所以会导致,两个线程拿到的锁都是相同的,所以会造成B阻塞.这是String常量池引起的,这种情况,可以把变量改为Object类型,这样就不是一个锁了,因为new Objectr()不在缓存里面。所以第二次会new 一个新的对象。
1.8 死锁就是双方互相持有对方锁的情况,比如A执行需要用到B对象,但是B执行也需要用到A对象。同时需要,则会造成死锁,切记,死锁和死循环是两种不同的情况,死循环是不断地重复,死锁是等待对方释放资源。
2. Volatile关键字
Volatile变量增加了实例变量在多个线程的可见性。但volatile关键字最致命的是不支持原子性,volatile只能修饰变量,synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
package thread;
public class HaselfPrivateNum extends Thread{
volatile public static int num ;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
num++;
}
System.out.println("num = " + num);
}
}
package thread;
public class Run {
public static void main(String[] args) {
HaselfPrivateNum []haselfPrivateNum = new HaselfPrivateNum[100];
for (int i = 0; i < 100; i++) {
haselfPrivateNum[i] = new HaselfPrivateNum();
}
for (int i = 0; i < 100; i++) {
haselfPrivateNum[i].start();
}
}
}
结果展示:
num = 3600
num = 3900
num = 4000
num = 4100
num = 4200
num = 4300
num = 4400
num = 4500
num = 4600
增加了线程之间的可见性,但是不具备同步性!关键字valitile保证了线程每次从共享内存中读取数据,而不是从私有内存中读取数据,这样保证了同步数据的可见性