文章22 | 阅读 8040 | 点赞0
我们先来创建一个业务类 TestService,代码如下:
public class TestService {
private int num;
public void addName(String username) {
try {
if ("a".equals(username)) {
num = 100;
System.out.println("ThreadA is over!");
Thread.sleep(1000);
System.out.println("ThreadA num = " + num);
} else {
num = 200;
System.out.println("ThreadB is over!");
System.out.println("ThreadB num = " + num);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
再创建两个线程类 ThreadA 和 ThreadB,代码如下:
public class ThreadA extends Thread {
private TestService service;
public ThreadA(TestService service) {
this.service = service;
}
@Override
public void run(){
service.addName("a");
}
}
public class ThreadB extends Thread {
private TestService service;
public ThreadB(TestService service) {
this.service = service;
}
@Override
public void run(){
service.addName("b");
}
}
最后创建运行类 Run,代码如下:
public class Run {
public static void main(String[] args) {
TestService service = new TestService();
ThreadA threadA = new ThreadA(service);
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.start();
}
}
控制台输出如下:
ThreadA is over!
ThreadB is over!
ThreadB num = 200
ThreadA num = 200
从控制台输出可以看出,执行结果和我们预想的不一样,这是因为两个线程并发访问业务对象中的变量 num,所以出现了线程不安全的问题,那么我们该如何解决这个问题呢?很简单,只需要加上关键字 sychronized 即可。
我们只需要在 TestService 类中的 addName(String username) 方法前加上 sychronized 关键字即可,代码如下:
public synchronized void addName(String username) {
try {
if ("a".equals(username)) {
num = 100;
System.out.println("ThreadA is over!");
Thread.sleep(1000);
System.out.println("ThreadA num = " + num);
} else {
num = 200;
System.out.println("ThreadB is over!");
System.out.println("ThreadB num = " + num);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
控制台输出如下:
ThreadA is over!
ThreadA num = 100
ThreadB is over!
ThreadB num = 200
我们可以把 addName(String username) 方法中的部分代码写成代码块格式,然后对代码块加锁,代码如下:
public void addName(String username) {
try {
synchronized (this) {
if ("a".equals(username)) {
num = 100;
System.out.println("ThreadA is over!");
Thread.sleep(1000);
System.out.println("ThreadA num = " + num);
} else {
num = 200;
System.out.println("ThreadB is over!");
System.out.println("ThreadB num = " + num);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
控制台输出如下:
ThreadA is over!
ThreadA num = 100
ThreadB is over!
ThreadB num = 200
在 Java 中只有“将对象作为锁”的说法,并没有“锁方法”的说法,我们可以理解成“锁”就是“对象”,“对象”可以映射为“锁”,哪个线程拿到这把锁,哪个线程就可以执行对应的 sychronized 同步方法或同步代码块。
在 Java 中,方法分为非静态方法和静态方法,在使用 sychronized 同步非静态方法和同步静态方法之间是不一样的,区别如下:
在使用 sychronized 同步非静态方法时,使用当前对象作为锁。
在使用 sychronized 同步静态方法时,使用当前静态方法所在类对应的 Class 单例对象作为锁。
假如某同步方法需要耗费大量时间执行,但其内部只有一小段代码涉及到线程安全的问题,这时就没必要使用 sychronized 同步方法了,我们只需要同步那一小段代码,即使用同步代码块。
在 Java 中,代码块同样分为非静态代码块和静态代码块,但是由于静态代码块不能定义在任何方法(包括静态方法)内,而 sychronized 必须作用于方法上或方法内部,所以静态代码块和 sychronized 是不可能会见面的,所以也就谈不上使用了,况且静态代码块只会在类加载时执行,且只会执行一次,也就谈不上线程安不安全的问题了。
同步代码块可以使用任意对象作为锁。
public void method() {
synchronized (object) {
// object 可以是任何对象,如:this,类对象,字符串对象等所有对象
}
}
sychronized 具有锁重入的功能,即在一个线程得到一个对象锁后,再次请求此对象锁是可以得到该对象锁的,这也说明在一个 sychronized 方法 / 代码块内部调用本类的其他 sychronized 方法 / 代码块时,时永远可以得到锁的。
锁重入支持继承的环境,当存在父子类继承关系时,子类可以通过锁重入调用父类的同步方法,示例代码如下:
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
Son son = new Son();
son.sonMethod();
}
}
class Son extends Person {
public synchronized void sonMethod() {
while (i > 0) {
i--;
System.out.println("Son " + i);
super.personMethod();
}
}
}
class Person {
public int i = 10;
public synchronized void personMethod() {
i--;
System.out.println("Person " + i);
}
}
控制台输出如下:
Son 9
Person 8
Son 7
Person 6
Son 5
Person 4
Son 3
Person 2
Son 1
Person 0
重写方法如果不使用 synchronized 关键字,就不是同步方法了,可能会出现线程安全问题,在使用了 sychronized 方法后,才会变成同步方法。
常用写法代码如下:
public class MyService {
public synchronized static void method1() {
}
public void method2() {
synchronized (MyService.class) {
}
}
public synchronized void method3() {
}
public void method4() {
synchronized (this) {
}
}
public void method5() {
synchronized ("hello") {
}
}
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_41685207/article/details/109406067
内容来源于网络,如有侵权,请联系作者删除!