Java多线程的重要性不言而喻,在多线程中有一个重要的问题就是共享资源的问题,synchronized关键字就是其中一个,这篇文章简要介绍下synchronized同步代码块的一些知识点。
synchronized的作用
- 多线程情况下互斥访问共享资源
- 共享资源的修改及时可见
- 有效解决重排序问题
synchronized锁对象
synchronized锁对象主要有如下三种:
- 普通同步方法,锁的是当前的实例对象
- 静态同步方法,锁的是当前的Class对象
- 同步代码块锁的是括号里面的对象
下面以多线程抢票案例,将逐个举例
(1)普通同步方法
public class MutilaTion {
public static void main(String[] args) throws InterruptedException {
BuyTicket buyTicket = new BuyTicket();
// 四个线程开始抢票
for (int i = 0; i < 4; i++){
new Thread(buyTicket).start();
}
}
}
class BuyTicket implements Runnable {
private int ticket = 10;
public synchronized void sale() {
if(ticket > 0){
ticket--;
System.out.println("线程:" + Thread.currentThread().getId() + "购票,剩余" + ticket + "张票");
}
}
@Override
public void run() {
while(ticket > 0){
try {
sale();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
代码执行结果如下:
现在我们开始分析代码:首先我们定义买票类,然后再main方法里面使用该买票类实例化四个线程,由于我们重写run方法,只有当票数小于等于0的时候才能结束线程;四个线程进行抢票,在run方法中调用了同步方法sale;因为sale方法被synchronized修饰,一旦进入该方法,BuyTicket类的实例对象(buyTicket对象)就被锁住,只有sale方法执行完毕然后才释放锁,一旦释放锁后,就有CPU来决定接下来执行哪个线程,保证了每个线程取得的ticket属性是互斥的,每次的操作都是原子性的。
现在我们将上述for循环修改,改为如下
for (int i = 0; i < 4; i++){
new Thread(new BuyTicket()).start();
}
程序执行结果如下:
分析结果可知,synchronized关键字用在普通方法上锁定的是类的实例化对象,同一对象才会阻塞,不同对象则不存在阻塞。
(2)静态同步方法
现在我们修改代码为static静态方法
private static int ticket = 10;
public static synchronized void sale() {
if(ticket > 0){
ticket--;
System.out.println("线程:" + Thread.currentThread().getId() + "购票,剩余" + ticket + "张票");
}
}
for循环为
public static void main(String[] args) throws InterruptedException {
// 四个线程开始抢票
for (int i = 0; i < 4; i++){
new Thread(new BuyTicket()).start();
}
}
执行结果如下:
由结果可知,程序是存在同步阻塞的,static代码块锁定的是Class类对象。
(3)同步代码块
synchronized(类名.class)为类锁,则无论是都相同对象调用都为同步阻塞.
synchronized(this)为对象锁,不通对象调用时不会阻塞,相同对象调用时为同步阻塞。
private int ticket = 10;
public void sale() {
synchronized (this){
if(ticket > 0){
ticket--;
System.out.println("线程:" + Thread.currentThread().getId() + "购票,剩余" + ticket + "张票");
}
}
}
public static void main(String[] args) throws InterruptedException {
BuyTicket buyTicket = new BuyTicket();
// 四个线程开始抢票
for (int i = 0; i < 4; i++){
new Thread(buyTicket).start();
}
}
执行结果:
若修改for循环为
public static void main(String[] args) throws InterruptedException {
// 四个线程开始抢票
for (int i = 0; i < 4; i++){
new Thread(new BuyTicket()).start();
}
}
执行结果为
可知锁定的是类实例对象
synchronized(类名.class)同上,就不贴代码了。
总结
对象锁:同一个对象实例会同步阻塞,不同对象实例则不会。
类锁:无论是否同一对象均为同步阻塞。
同一个类多个方法被synchronized修饰/代码块/静态方法,则在各个方法之间也是同步阻塞的。