synchronized修饰方法、static方法、this、.class、obj的代码示例

对象锁

一、非静态方法,方法头部加入synchronized关键字,例如:public synchronized void printC() {...}。不同线程访问同一个对象的该方法时,会产生互斥。在jdk中,StringBuffer的append()方法就属于这种类型。

public class PrintTool {

    public synchronized void printC()  {
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" " + Thread.currentThread().getName() + "获得锁");
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " " + Thread.currentThread().getName() + "释放锁");
    }
}

public class MyThread extends Thread {
    PrintTool pt;

    public MyThread(PrintTool pt, String name) {
        super(name);
        this.pt = pt;
    }

    @Override
    public void run() {
        this.pt.printC();
    }
}

public class SyncTest {

    public static void main(String[] args) {
        PrintTool pt = new PrintTool();

        for (int i = 0; i < 5; i++) {
           new MyThread(pt, "t" + i).start();
        }
    }
}

运行结果:

可以看到,t0最先获得锁,后面的每一个线程,只能等待上一个线程释放锁之后,才能执行这个方法。

二、在非static方法的方法体中,加入synchronized(obj){...}代码块。拥有同一个指定对象obj的多个线程,如果同时执行这段代码块,会产生互斥。这种写法,常见于自定义线程中,多个线程同时修改一个对象。经典的多线程卖票就是这种情况。

public class PrintTool {

    public void printC()  {
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" " + Thread.currentThread().getName() + "获得锁");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " " + Thread.currentThread().getName() + "释放锁");
    }
}

public class SyncTest {

    public static void main(String[] args) {
        PrintTool pt = new PrintTool();
        for (int i = 1; i <=3; i++) {
           new Thread(new Runnable(){
               @Override
               public void run() {
                   synchronized (pt) {
                       pt.printC();
                   }
               }
           }, "t" + i).start();
        }
    }
}

运行结果:

每隔两秒,会有一个线程获得锁。而不是t1,t2,t3同时进入方法。
对象锁必须是同一个对象,才起作用。如果是不同对象,就无效。把上面的代码SyncTest修改如下,看看结果:

public class SyncTest {
    public static void main(String[] args) {
        for (int i = 1; i <=3; i++) {
           new Thread(new Runnable(){
               @Override
               public void run() {
                   PrintTool pt = new PrintTool();
                   synchronized (pt) {
                       pt.printC();
                   }
               }
           }, "t" + i).start();
        }
    }
}

可以看到,3个线程同时获得了三个不同的锁,2秒后,又同时释放。synchronized就失效了。

三、在一个普通类(线程类除外)的非static方法的方法体中,使用synchronized(this){...}代码块。拥有该普通类的同一个对象的多个线程,同时访问这个方法中的同步代码块时,会产生互斥。这种写法的场景和第二种有些不同,这种写法,经常是用于多个线程只修改同一个的对象的某一个属性,其他属性不用考虑多线程的情况,synchronized使用在同步类的内部,而不是其他类和线程的run方法中。

public class PrintTool {

    public void printC() {
        synchronized (this) {
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " " + Thread.currentThread().getName() + "进入同步代码块");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " " + Thread.currentThread().getName() + "释放锁");
        }
    }
}

public class SyncTest {
    public static void main(String[] args) {
        PrintTool pt = new PrintTool();
        for (int i = 1; i <=3; i++) {
           new Thread(new Runnable(){
               @Override
               public void run() {
                   pt.printC();
               }
           }, "t" + i).start();
        }
    }
}

运行结果:

后面的线程等待前一个线程释放锁后,才能执行同步代码。
如果是不同对象,synchronized就会失效。SyncTest代码修改如下:

public class SyncTest {
    public static void main(String[] args) {
        for (int i = 1; i <=3; i++) {
           new Thread(new Runnable(){
               @Override
               public void run() {
                   PrintTool pt = new PrintTool();
                   pt.printC();
               }
           }, "t" + i).start();
        }
    }
}

从结果看出,同步失效了。原因是三个线程拥有三个不同的对象,拥有三个不同的锁。

类锁

先看看,在不使用synchronized关键字的情况下,多个线程同时执行一个类的static方法,会出现什么情况。

public class PrintTool {

    public static void printStaticNormal()  {
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" " + Thread.currentThread().getName() + "开始执行");
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " " + Thread.currentThread().getName() + "结束执行");
    }
}

public class SyncTest {

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
           new Thread(new Runnable(){
               @Override
               public void run() {
                   PrintTool.printStaticNormal();
               }
           }, "t" + i).start();
        }
    }
}

运行结果:

可以看到,多个线程同时进入这个方法,并不是等待一个线程执行完之后,另一个线程再开始执行。
下面探索synchronized类锁的作用:

一、static方法体中,使用synchronized(XX.class){...}。不同线程访问同一个类的static方法时,不管是不是同一个方法,都会产生互斥。这种写法,常见于单例模式。

public class PrintFruit {
    public static void printA(){
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" Apple");
    }

    public static void printB(){
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" Banana");

    }
}

public class SyncTest {
    public static void main(String[] args) {
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                synchronized (PrintFruit.class) {
                    PrintFruit.printA();
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        Thread t1 = new Thread(r1, "t1");
        Thread t2 = new Thread(r1,"t2");
        Thread t3 = new Thread(r1, "t3");

        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                synchronized (PrintFruit.class) {
                    PrintFruit.printB();
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread t4 = new Thread(r2, "t4");
        Thread t5 = new Thread(r2, "t5");
        Thread t6 = new Thread(r2, "t6");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}

运行结果:

可以根据时间间隔看出,每隔3秒,下一个线程才开始执行代码块。即使printA和printB是两个不同的方法,也会出现互斥。

二、在static方法头中,加入synchronized关键字。不同线程同时访问该类的static方法,即使是不同方法,也会产生互斥。

public class PrintTool {

    public synchronized static void printC()  {
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" " + Thread.currentThread().getName() + "获得锁");
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " " + Thread.currentThread().getName() + "释放锁");
    }

    public synchronized  static  void printD()  {
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" " + Thread.currentThread().getName() + "获得锁");
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " " + Thread.currentThread().getName() + "释放锁");
    }
}

public class SyncTest {

    public static void main(String[] args) {
        for (int i = 1; i <=3; i++) {
           new Thread(new Runnable(){
               @Override
               public void run() {
                   PrintTool.printC();
               }
           }, "t" + i).start();
        }

        for (int i = 4; i <=6; i++) {
           new Thread(new Runnable(){
               @Override
               public void run() {
                   PrintTool.printD();
               }
           }, "t" + i).start();
        }
    }
}

运行结果:

t1,t2,t3执行的是printC,t4,t5,t6执行的是printD。可以看到,即使是不同方法,也会出现互斥。

感谢:
synchronized(this) 与synchronized(class) 之间的区别
synchronized的四种用法
Java之Synchronized修饰实例方法和静态方法
Synchronized同步静态方法和非静态方法总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值