java关键字synchronized_Javasynchronized关键字详解

sap java开发技术详解&mdash基础

94.01元

(需用券)

去购买 >

941760e220116e4f451930e5de65be9b.png

Java synchronized 关键字详解

前置技能点

进程和线程的概念

线程创建方式

线程的状态状态转换

线程安全的概念

synchronized 关键字的几种用法

修饰非静态成员方法

synchronized public void sync(){

}

修饰静态成员方法

synchronized public static void sync(){

}

类锁代码块

synchronized (类.class){

}

对象锁代码块

synchronized (this|对象){

}

synchronized 修饰非静态方法时可以看做是锁 this 对象,修饰静态方法时可以看做是锁方法所在的类。

synchronized 关键字的根本机制

各个线程想要访问被 synchronized 修饰的代码块,就要取得 synchronized 声明的锁。如果两个线程的目标是同一个锁,就会出现阻塞的现象,所以两个线程不能同时访问同一个锁下的代码,保证了多线程在执行时最终结果不会出错。这与共享变量是否为静态无关。

几个例子

对象锁

public class ThreadDemo extends Thread {

@Override

public synchronized void run() {

for (int i = 0; i < 10000; i++) {

Main.i++;

}

System.out.println("执行完成");

}

}

直接将继承的 run() 方法标记为 synchronized ,作用是对 Main 类中的 i 变量做 10000 次累加操作。

public class Main {

static int i = 0;

public static void main(String[] args) throws InterruptedException {

ThreadDemo threadDemo=new ThreadDemo();

Thread t1 = new Thread(threadDemo);

Thread t2 = new Thread(threadDemo);

Thread t3 = new Thread(threadDemo);

Thread t4 = new Thread(threadDemo);

t1.start();

t2.start();

t3.start();

t4.start();

t1.join();

t2.join();

t3.join();

t4.join();

System.out.println(i);

}

}

//输出结果:

//执行完成

//执行完成

//执行完成

//执行完成

//40000

可以看到当4个线程全部执行完毕之后,变量 i 成功的累加了 40000 次,没有出现丢失操作的情况。

如果我们将 main() 方法修改如下:

public static void main(String[] args) throws InterruptedException {

Thread t1 = new ThreadDemo();

Thread t2 = new ThreadDemo();

Thread t3 = new ThreadDemo();

Thread t4 = new ThreadDemo();

t1.start();

t2.start();

t3.start();

t4.start();

t1.join();

t2.join();

t3.join();

t4.join();

System.out.println(i);

}

//输出结果:

//执行完成

//执行完成

//执行完成

//执行完成

//27579

可以看到丢失了不少的累加操作。观察前后两个 main() 方法创建线程的方式可以发现,前面的 main() 方法是使用了同一个对象来创建了4个不同的线程,而后一个 main() 方法使用了4个不同的 ThreadDemo 对象创建了4个线程。我们用 synchronized 修饰的是一个非静态成员函数,相当于对该方法创建了 this 的对象锁。在第一个 main() 方法中使用同一个对象来创建 4 个不同线程就会让 4 个线程争夺同一个对象锁,这样,在同一时间内,仅能有一个线程能访问 synchronized 修饰的方法。而在第二种 main() 方法中,4 个线程各自对应一个对象锁,4 个线程之间没有竞争关系,对象锁自然无法生效。

类锁

public class ThreadDemo extends Thread {

@Override

public void run() {

synchronized (ThreadDemo.class) {

for (int i = 0; i < 10000; i++) {

Main.i++;

}

System.out.println("执行完成");

}

}

}

将修饰方法的 synchronized 改为对 ThreadDemo.class 上锁的代码块

public class ThreadDemo2 extends Thread {

@Override

public void run() {

synchronized (ThreadDemo2.class) {

for (int i = 0; i < 10000; i++) {

Main.i++;

}

System.out.println("执行完成");

}

}

}

再创建一个相同的类命名为 ThreadDemo2 ,与 ThreadDemo 不同的是,ThreadDemo2 中,synchronized 对 ThreadDemo2.class 上锁。

public static void main(String[] args) throws InterruptedException {

Thread t1 = new ThreadDemo();

Thread t2 = new ThreadDemo();

Thread t3 = new ThreadDemo2();

Thread t4 = new ThreadDemo2();

t1.start();

t2.start();

t3.start();

t4.start();

t1.join();

t2.join();

t3.join();

t4.join();

System.out.println(i);

}

//输出结果:

//执行完成

//执行完成

//执行完成

//执行完成

//33054

4 个线程分别由 ThreadDemo 和 ThreadDemo2 来创建,显然得到的结果与预期的 40000 不符。如果我们将 ThreadDemo2 中的 synchronized 改为对 ThreadDemo.class 上锁:

public class ThreadDemo2 extends Thread {

@Override

public void run() {

synchronized (ThreadDemo.class) {

for (int i = 0; i < 10000; i++) {

Main.i++;

}

System.out.println("执行完成");

}

}

}

//输出结果:

//执行完成

//执行完成

//执行完成

//执行完成

//40000

可以看到,虽然是声明在两个不同的类中的 synchronized 代码块,但是由于都是对 ThreadDemo.class 上锁,所以 4 个线程之间还是建立了竞争关系,同时只能有一个线程访问被 synchronized 修饰的代码。

总结

所以 synchronized 关键字的本质是限制线程访问一段代码,而限制的条件就是,在所有被加上相同锁的代码上,同一时间,只能有一个线程在运行。这与你要修改什么样的共享变量无关。在我刚接触到的时候以为类锁和对象锁是分别针对静态共享变量和非静态共享变量的,但事实上锁的是要执行的代码块,而不是代码块将要访问的共享变量。

java 11官方入门(第8版)教材

79.84元

包邮

(需用券)

去购买 >

f0f3f55624fb396b1764d42d6df88864.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值