美文网首页
方法锁、类锁和synchronized代码块

方法锁、类锁和synchronized代码块

作者: 凉风拂面秋挽月 | 来源:发表于2020-01-05 21:12 被阅读0次

方法锁(对象锁)

修饰在实例方法上,多个线程调用同一个对象的同步方法会阻塞(不管同步方法是不是同一个,只要对象是同一个就行),调用不同对象的同步方法不会阻塞。
简单来说,锁住的仅仅是一个类中的一个方法,该方法在同一时刻只能被一个线程调用。相对于方法锁,对象锁这个名字更为合适,因为只要有线程在调用该类的同步方法,其他线程调用本类的任何同步方法都会无效。
示例:
具有方法锁的测试类

public class Sync {
    public synchronized void test(String name) {
        
        System.out.println(name+"test开始.."+System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+"test结束.."+System.currentTimeMillis());
    }
    public synchronized void test2(String name) {
        System.out.println(name+"test2开始.."+System.currentTimeMillis());
       try {
            Thread.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }      
        System.out.println(name+"test2结束.."+System.currentTimeMillis());
    }
}

两个线程类,分别调用Asyc的两个不同的同步方法

public class MyThread1 extends Thread{
    private Sync sync;

    public MyThread1(Sync sync) {
        this.sync = sync;
        this.setName("线程1");
    }
    @Override
    public void run() {     
        sync.test(currentThread().getName());
    }
}
public class MyThread2 extends Thread{
    private Sync sync;

    public MyThread2(Sync sync) {
        this.sync = sync;
        this.setName("线程2");
    }
    @Override
    public void run() {
        sync.test2(currentThread().getName());
    }
}

测试

public class Test {
    public static void main(String[] args) {
        Sync sync = new Sync();
        MyThread1 thread = new MyThread1(sync);
        MyThread2 thread2 = new MyThread2(sync);
        thread.start();
        thread2.start();
    }
}

运行结果:

线程2test2开始..1578217624004
线程2test2结束..1578217624804
线程1test开始..1578217624804
线程1test结束..1578217625805

ps:调用非同步方法

正如一开始所说,我们的方法锁会让整个类的所有同步方法都锁定,那么在A线程调用同步方法的时候,B线程调用该对象的非同步方法会不会阻塞?
测试一下,我们去掉test1的synchronized关键字

public class Sync {
    public void test(String name) {     
        System.out.println(name+"test开始.."+System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+"test结束.."+System.currentTimeMillis());
    }
    public synchronized void test2(String name) {
        System.out.println(name+"test2开始.."+System.currentTimeMillis());
       try {
            Thread.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }      
        System.out.println(name+"test2结束.."+System.currentTimeMillis());
    }
}

执行结果

线程2test2开始..1578222537075
线程1test开始..1578222537075
线程2test2结束..1578222537875
线程1test结束..1578222538075

答案是对线程A调用同步方法对线程B调用同一对象的非同步方法没有影响。

类锁

修饰在静态方法上,多个线程调用同一个类的同步方法会阻塞(不管同步方法是不是同一个,也不管是不是同一个对象,只要是同一个类就会阻塞)。
简单来说,类锁相对于方法锁,更加严格,即使是不同对象,只要有线程调用同一个类中任意一个对象的任意一个同步方法,都会导致其他线程调用该类的实例的同步方法阻塞。
实例:
具有类锁的测试类

public class Sync {
public static synchronized void test(String name) {
        
        System.out.println(name+"test开始.."+System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+"test结束.."+System.currentTimeMillis());
    }
}

两个工具线程

public class MyThread1 extends Thread{
    public MyThread1() {
        this.setName("线程1");
    }
    @Override
    public void run() {     
        Sync.test(currentThread().getName());
    }
}
public class MyThread2 extends Thread{
    public MyThread2() {
        this.setName("线程2");
    }
    @Override
    public void run() {
        Sync.test(currentThread().getName());
    }
}

测试

public class Test {
    public static void main(String[] args) {
        MyThread1 thread = new MyThread1();
        MyThread2 thread2 = new MyThread2();
        thread.start();
        thread2.start();
    }
}

运行结果

线程2test开始..1578226459350
线程2test结束..1578226460352
线程1test开始..1578226460352
线程1test结束..1578226461352

由于是静态方法,所以在两个线程中就不需要用实例调用了,最终结果说明类锁阻塞了试图调用被加锁的类中的同步方法。

ps再次验证非同步方法

1.我们对Asyc类增加一个非同步方法再次测试,让线程2去调非同步方法。
更改如下

public class Sync {
public static synchronized void test(String name) { 
        System.out.println(name+"test开始.."+System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+"test结束.."+System.currentTimeMillis());
    }
public void test2(String name) {
    
    System.out.println(name+"test2开始.."+System.currentTimeMillis());
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(name+"test2结束.."+System.currentTimeMillis());
}
}

测试类

public class Test {
    public static void main(String[] args) {
        Sync sync2= new Sync();
        MyThread1 thread = new MyThread1();
        MyThread2 thread2 = new MyThread2(sync2);
        thread.start();
        thread2.start();
    }
}

运行结果:

线程2test2开始..1578227227114
线程1test开始..1578227227114
线程2test2结束..1578227228115
线程1test结束..1578227228115

结果表明,类锁同样对非同步方法无作用。

2.如果对Asyc类增加一个方法锁呢。
更改如下:

public class Sync {
public static synchronized void test(String name) {
        
        System.out.println(name+"test开始.."+System.currentTimeMillis());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+"test结束.."+System.currentTimeMillis());
    }
public synchronized void test2(String name) {
    
    System.out.println(name+"test2开始.."+System.currentTimeMillis());
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(name+"test2结束.."+System.currentTimeMillis());
}
}

测试类不做更改,运行结果:

线程2test2开始..1578227470062
线程1test开始..1578227470062
线程2test2结束..1578227471062
线程1test结束..1578227471062

结果表明,类锁对方法锁无效,类锁锁住的只有静态同步方法。经过测试,同理,方法锁对类锁也无效,方法锁锁住的只有实例方法

synchronized代码块

使用synchronized声明的方法在某些情况下是有弊端的,比如A线程调用同步的方法执行一个长时间的任务,那么B线程就必须等待比较长的时间才能执行,这种情况可以使用synchronized代码块去优化代码执行时间,只有在必须锁住对象的时候再上锁,也就是通常所说的减少锁的粒度。
ps:synchronized(Object)相当于一个对象锁/方法锁。
示例:
下面用实例说明,在方法内部有两次sleep,代表两次长时间任务,只有在第二次任务时在用同步代码块上锁。

public class Asyc {
    public void doLongTimeTask(){
        try {
            
            System.out.println("当前线程开始:" + Thread.currentThread().getName() + 
                    ", 正在执行一个较长时间的业务操作,其内容不需要同步");
            Thread.sleep(2000);         
            synchronized(this){
                System.out.println("当前线程:" + Thread.currentThread().getName() + 
                    ", 执行同步代码块,对其同步变量进行操作");
                Thread.sleep(1000);
            }
            System.out.println("当前线程结束:" + Thread.currentThread().getName() +
                    ", 执行完毕");
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        final Asyc asyc = new Asyc();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                asyc.doLongTimeTask();
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                asyc.doLongTimeTask();
            }
        },"t2");
        t1.start();
        t2.start();     
    }
}

执行结果如下:

当前线程开始:t2, 正在执行一个较长时间的业务操作,其内容不需要同步
当前线程开始:t1, 正在执行一个较长时间的业务操作,其内容不需要同步
当前线程:t2, 执行同步代码块,对其同步变量进行操作
当前线程结束:t2, 执行完毕
当前线程:t1, 执行同步代码块,对其同步变量进行操作
当前线程结束:t1, 执行完毕

效果显而易见,不加说明了,synchronized(this)也就是表明锁住当前调用这个方法的实例,跟方法锁一样。

ps:本例如果不用同步代码块也可用方法锁做优化,只需要把不需要执行同步的部分抽出来当成一个独立的方法即可。不写了。

相关文章

  • Java中synchronized原理

    前言 synchronized可以修饰方法和代码块。synchronized需要持有一个对象锁。分为类锁和对象锁。...

  • 并发编程(二)初识synchronized与ReentrantL

    Synchronized修饰方法,代码块类锁,对象锁 synchronized实现原理:monitorenter与...

  • 方法锁、类锁和synchronized代码块

    方法锁(对象锁) 修饰在实例方法上,多个线程调用同一个对象的同步方法会阻塞(不管同步方法是不是同一个,只要对象是同...

  • synchronized 关键字里的锁

    synchronized 关键字的锁有静态态方法锁,锁是所在类的class文件,非静态方法里代码块的synchr...

  • synchronized底层实现

    java里synchronized锁分为方法锁和代码块锁两种 静态方法默认以class对象作为锁,普通方法默认以对...

  • Synchronized的实现原理

    Synchronized 锁的两种方式分别的针对方法的锁和针对代码块的锁。 代码块锁 首先,我们用javap -c...

  • 解决多线程安全问题的锁方式

    synchronized:隐式锁 同步代码块 同步方法示例代码,略。 jdk 1.5 后,同步锁 Lock 同步锁...

  • 并发

    锁:方法锁、对象锁、类锁1.方法锁(synchronized修饰方法时):synchronized放在方法和在方法...

  • Synchronized 作用域

    如图,synchronized可以用在方法和代码块中,被锁对象的区别: 1. 如果锁的是类对象的话,尽管new多个...

  • synchronized(修饰方法和代码块)

    synchronized(修饰方法和代码块) 1. 含义 synchronized 是同步锁,用来实现互斥同步。 ...

网友评论

      本文标题:方法锁、类锁和synchronized代码块

      本文链接:https://www.haomeiwen.com/subject/gwhcactx.html