『线程安全』与『非线程安全』是学习多线程技术时一定会遇到的经典问题。『非线程安全』其实当多个线程访问同一个对象中的成员变量时产生的,产生的后果就是『脏读』,就是取到的数据其实是被更改过的。而『线程安全』就是以获取的成员变量的值是经过同步处理的,不会出现脏读的现象。
局部变量是线程安全的
public class Demo01 {
public static void main(String[] args) {
Demo01Service service = new Demo01Service();
Thread t1 = new Demo01ThreadA(service);
t1.start();
Thread t2 = new Demo01ThreadB(service);
t2.start();
}
}
class Demo01Service{
public void add(String username){
int num = 0;
if ("a".equals(username)){
num = 100;
System.out.println("a set over");
// 等待另外一个线程修改num的值
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
num = 200;
System.out.println("b set over");
}
System.out.println("username=" + username + ", num=" + num);
}
}
class Demo01ThreadA extends Thread{
private Demo01Service service;
public Demo01ThreadA(Demo01Service service){
this.service = service;
}
@Override
public void run() {
service.add("a");
}
}
class Demo01ThreadB extends Thread{
private Demo01Service service;
public Demo01ThreadB(Demo01Service service){
this.service = service;
}
@Override
public void run() {
service.add("b");
}
}
局部变量不存在非线程安全问题,永远都是线程安全的。这是由局部变量是私有的特征所造成的。
成员变量不是线程安全
public class Demo02 {
public static void main(String[] args) {
Demo02Service service = new Demo02Service();
Thread t1 = new Demo02ThreadA(service);
Thread t2 = new Demo02ThreadB(service);
t1.start();
t2.start();
}
}
class Demo02Service {
private int num;
synchronized public void add(String username){
if ("a".equals(username)){
num = 100;
System.out.println("a is set");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
num = 200;
System.out.println("b is set");
}
System.out.println("username=" + username + ", num=" + num);
}
}
class Demo02ThreadA extends Thread{
private Demo02Service service;
public Demo02ThreadA(Demo02Service service){
this.service = service;
}
@Override
public void run() {
service.add("a");
}
}
class Demo02ThreadB extends Thread{
private Demo02Service service;
public Demo02ThreadB(Demo02Service service){
this.service = service;
}
@Override
public void run() {
service.add("b");
}
}
如果有两个线程同时操作业务对象中的成员变量,可能会产生『非线程安全』问题,需要在方法前使用关键字synchronized进行修饰。
多个对象使用多个对象锁
public class Demo03 {
public static void main(String[] args) {
Demo03Service service1 = new Demo03Service();
Demo03Service service2 = new Demo03Service();
Thread t1 = new Demo03ThreadA(service1);
Thread t2 = new Demo03ThreadB(service2);
t1.start();
t2.start();
}
}
class Demo03Service {
private int num;
synchronized public void add(String username){
if ("a".equals(username)){
num = 100;
System.out.println("a is set");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
num = 200;
System.out.println("b is set");
}
System.out.println("username=" + username + ", num=" + num);
}
}
class Demo03ThreadA extends Thread{
private Demo03Service service;
public Demo03ThreadA(Demo03Service service){
this.service = service;
}
@Override
public void run() {
service.add("a");
}
}
class Demo03ThreadB extends Thread{
private Demo03Service service;
public Demo03ThreadB(Demo03Service service){
this.service = service;
}
@Override
public void run() {
service.add("b");
}
}
synchronized取得的锁都是对象锁,而不是把一段代码或方法作为锁,所以那个线程先执行带synchronized关键字修饰的方法,哪个方法就持有该方法所属对象的锁,其它线程只能呈等待状态,前提是多个线程访问的是同一个对象。如果多个线程访问多个对象,JVM会创建出多个对象锁。