学科分类
目录
Java基础

同步方法

通过前面小节的学习,了解到同步代码块可以有效解决线程的安全问题,当把共享资源的操作放在synchronized定义的区域内时,便为这些操作加了同步锁。同样,在方法前面也可以使用synchronized关键字来修饰,被修饰的方法为同步方法,它能实现和同步代码块同样的功能,具体语法格式如下:

[修饰符] synchronized 返回值类型 方法名([参数1,……]){}

被synchronized修饰的方法在某一时刻只允许一个线程访问,访问该方法的其他线程都会发生阻塞,直到当前线程访问完毕后,其他线程才有机会执行。

接下来使用同步方法模拟售票系统,如文件1所示。

文件1 Example13.java

 1    // 定义SaleThread3类实现Runnable接口
 2    class SaleThread3 implements Runnable {
 3        private int tickets = 10;
 4        public void run() {
 5            while (true) {
 6                saleTicket(); // 调用售票方法
 7            }
 8        }
 9        // 定义一个同步方法saleTicket()
 10        private synchronized void saleTicket() {
 11            if (tickets > 0) {
 12                try {
 13                    Thread.sleep(100); // 模拟售票耗时过程
 14                } catch (InterruptedException e) {
 15                    e.printStackTrace();
 16                }
 17                System.out.println(Thread.currentThread().getName() 
 18                                     + " 正在发售第 " + tickets-- + " 张票 ");
 19            }
 20        }
 21    }
 22    public class Example13 {
 23        public static void main(String[] args) {
 24            SaleThread3 saleThread = new SaleThread3(); 
 25            // 创建并开启四个线程,模拟4个售票窗口
 26            new Thread(saleThread, "窗口1").start();
 27            new Thread(saleThread, "窗口2").start();
 28            new Thread(saleThread, "窗口3").start();
 29            new Thread(saleThread, "窗口4").start();
 30        }
 31    }

运行结果如图1所示。

图1 运行结果

文件1中,将售票代码抽取为售票方法saleTicket(),并用synchronized关键字把saleTicket()修饰为同步方法,然后在run()方法中调用该方法。从图1可以看出,同样没有出现0号和负数号的票,说明同步方法实现了和同步代码块一样的效果。

思考:

大家可能会有这样的疑问:同步代码块的锁是自己定义的任意类型的对象,那么同步方法是否也存在锁?如果有,它的锁是什么呢?答案是肯定的,同步方法也有锁,它的锁就是当前调用该方法的对象,也就是this指向的对象。这样做的好处是,同步方法被所有线程所共享,方法所在的对象相对于所有线程来说是唯一的,从而保证了锁的唯一性。当一个线程执行该方法时,其他的线程就不能进入该方法中,直到这个线程执行完该方法为止,从而达到了线程同步的效果。

有时候需要同步的方法是静态方法,静态方法不需要创建对象就可以直接用“类名.方法名()”的方式调用。这时候我们就会有一个疑问,如果不创建对象,静态同步方法的锁就不会是this,那么静态同步方法的锁是什么?Java中静态方法的锁是该方法所在类的class对象,该对象可以直接类名.class的方式获取。

同步代码块和同步方法解决多线程问题有好处也有弊端。同步解决了多个线程同时访问共享数据时的线程安全问题,只要加上同一个锁,在同一时间内只能有一条线程执行,但是线程在执行同步代码时每次都会判断锁的状态,非常消耗资源,效率较低。

点击此处
隐藏目录