1.sychronized的作用:
在并发编程中会存在线程安全问题,主要原因是存在共享数据和多线程共同操作共享数据。关键字sychronized可以保证在同一时刻只有一个线程可以执行某个方法或某个代码块(临界区),同时synchronized可以保证一个线程的变化可见。
只有共享资源读写访问才需要同步化,如果不是共享资源就没有必要同步。
2.三种应用方式
- 修饰实例方法,对当前实例进行加锁,进入同步代码前需获得当前实例的锁
- 修饰静态方法,对类对象加锁,要先获得当前类对象的锁
- 修饰代码块,指定加锁对象,对给定对象加锁,要先获得给定对象的锁
2.1同步方法(静态方法、实例方法)
用sychronized修饰的方法就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
synchronized public void doWorlk(){
...
}
对于非静态类方法,同步锁就是this(实例对象)
对于静态类方法,同步锁是当前方法所在类的字节码对象(类对象)
2.2同步代码块
syschronized(同步锁){
....
}
同步锁:在任何时候最多只能有一个线程有同步锁,其他线程只能在代码块外等着。
为保证每个线程都能正常执行原子操作,Java引入了线程同步机制。java程序运行使用任何对象作为同步监听对象,但一般的,我们把当前并发访问的共同资源作为同步监听对象。
3.举个栗子:
package demo01;
public class test01 {
public static void main(String[] args) throws InterruptedException {
// 并发:多线程操作同一个资源类,
Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口
new Thread(() -> {
for (int i = 1; i < 20; i++) {
//把资源类丢入线程
ticket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 20; i++) {
ticket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 20; i++) {
ticket.sale();
}
}, "C").start();
}
}
class Ticket {
//共卖40张票
private int number = 30;
// 卖票的方式
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "票,剩余:" + number);
}
}
}
当多个线程同时对一个对象的一个方法进行操作,只有一个线程能够抢到锁。因为一个对象只有一把锁,一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,就不能访问该对象的其他synchronized实例方法,但是可以访问非synchronized修饰的方法。
节选自狂神说JUC学习笔记+补充(一)是其中的一部分,为方便阅读将其抽取出来,查看完整笔记见
演绎法5919:狂神说JUC学习笔记+补充(一)zhuanlan.zhihu.com![ea5057eb1f3d148502948e70f643baec.png](https://img-blog.csdnimg.cn/img_convert/ea5057eb1f3d148502948e70f643baec.png)