并发编程(二)之synchronized内置锁

synchronized内置锁

每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。

synchronized的使用

在学习使用synchronized前,我们先来看一段代码:

public class SyncTest {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void setCount(long count) {
        this.count = count;
    }


    public void incCount() {
        count++;
    }

    //线程
    private static class Count extends Thread {

        private SyncTest simplOper;

        public Count(SyncTest simplOper) {
            this.simplOper = simplOper;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                simplOper.incCount();//count = count+10000
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTest simplOper = new SyncTest();
        //启动两个线程
        Count count1 = new Count(simplOper);
        Count count2 = new Count(simplOper);
        count1.start();
        count2.start();
        Thread.sleep(50);
        System.out.println(simplOper.count);
    }
}

开启了2个线程,每个线程都累加了10000次,如果结果正确的话自然而然总数就应该是20000。可就运行多次结果都不是这个数,而且每次运行结果都不一样。使用synchronized内置锁就可以解决上面的问题。

作用在方法上

public class SyncTest {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void setCount(long count) {
        this.count = count;
    }

	//使用synchronized作用在方法上
    public synchronized void incCount() {
        count++;
    }

    //线程
    private static class Count extends Thread {

        private SyncTest simplOper;

        public Count(SyncTest simplOper) {
            this.simplOper = simplOper;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                simplOper.incCount();//count = count+10000
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTest simplOper = new SyncTest();
        //启动两个线程
        Count count1 = new Count(simplOper);
        Count count2 = new Count(simplOper);
        count1.start();
        count2.start();
        Thread.sleep(50);
        System.out.println(simplOper.count);
    }
}

打印结果

20000

作用在代码块上

public class SyncTest {
    private long count = 0;

    public long getCount() {
        return count;
    }

    public void setCount(long count) {
        this.count = count;
    }

	//使用synchronized作用在代码块上
    public void incCount() {
        synchronized(this){
            count++;
        }
    }

    //线程
    private static class Count extends Thread {

        private SyncTest simplOper;

        public Count(SyncTest simplOper) {
            this.simplOper = simplOper;
        }

        @Override
        public  void run() {
            for (int i = 0; i < 10000; i++) {
                simplOper.incCount();//count = count+10000
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTest simplOper = new SyncTest();
        //启动两个线程
        Count count1 = new Count(simplOper);
        Count count2 = new Count(simplOper);
        count1.start();
        count2.start();
        Thread.sleep(50);
        System.out.println(simplOper.count);
    }
}

对象锁和类锁

对象锁是用于对象实例方法,或者一个对象实例上的。类锁是用于类的静态方法或者一个类的 class 对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个 class 对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。

但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,类锁其实锁的是每个类的对应的 class 对象。类锁和对象锁之间也是互不干扰的。

对象锁

对象锁是用于对象实例方法,或者一个对象实例上

/**
 * 类说明:锁的实例不一样,也是可以并行的
 */
public class DiffInstance {

    private static class A implements Runnable {
        private DiffInstance diffInstance;

        public A(DiffInstance diffInstance) {
            this.diffInstance = diffInstance;
        }

        @Override
        public void run() {
            System.out.println("A is running..." + diffInstance.toString());
            diffInstance.instanceA();
        }
    }

    private static class B implements Runnable {
        private DiffInstance diffInstance;

        public B(DiffInstance diffInstance) {
            this.diffInstance = diffInstance;
        }

        @Override
        public void run() {
            System.out.println("B is running..." + diffInstance);
            diffInstance.instanceB();
        }
    }
    
    //作用于方法
    private synchronized void instanceA() {
        second(3);
        System.out.println("A is going..." + this.toString());
        second(3);
        System.out.println("A ended " + this.toString());
    }
	
	//作用于对象实例
    private synchronized void instanceB() {
        synchronized (this){
           second(3);
           System.out.println("B is going..." + this.toString());
           second(3);
           System.out.println("B ended " + this.toString());
       }
    }

    /**
     * 按秒休眠
     * @param seconds 秒数
     */
    public static final void second(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) {
        DiffInstance instance1 = new DiffInstance();
        Thread t1 = new Thread(new B(instance1));
        DiffInstance instance2 = new DiffInstance();
        Thread t2 = new Thread(new A(instance2));
        t1.start();
        t2.start();
    }
}

其中一次打印如下:

A is running...cn.wyhcsl.syn.DiffInstance@14e1774
B is running...cn.wyhcsl.syn.DiffInstance@469add4e
A is going...cn.wyhcsl.syn.DiffInstance@14e1774
B is going...cn.wyhcsl.syn.DiffInstance@469add4e
B ended cn.wyhcsl.syn.DiffInstance@469add4e
A ended cn.wyhcsl.syn.DiffInstance@14e1774

上面是执行结果,我们可以看到,结果输出是交替着进行输出的。说明锁的实例不一样,是可以并行的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4GkeneR-1593620304436)(http://img.wyhcsl.cn//blog/20200701220302057.png)]

如果把上面main方法中使用同一个对象

public static void main(String[] args) {
        DiffInstance instance1 = new DiffInstance();
        Thread t1 = new Thread(new B(instance1));
        //DiffInstance instance2 = new DiffInstance();
        Thread t2 = new Thread(new A(instance1));
        t1.start();
        t2.start();
        second(1);
}

其中一次打印如下:

A is running...cn.wyhcsl.syn.DiffInstance@3f455d86
B is running...cn.wyhcsl.syn.DiffInstance@3f455d86
A is going...cn.wyhcsl.syn.DiffInstance@3f455d86
A ended cn.wyhcsl.syn.DiffInstance@3f455d86
B is going...cn.wyhcsl.syn.DiffInstance@3f455d86
B ended cn.wyhcsl.syn.DiffInstance@3f455d86

上面是执行结果,我们可以看到一定要等某一个线程跑完之后,下一个才会接着执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0H9AUZ6V-1593620304441)(http://img.wyhcsl.cn//blog/20200701220430134.png)]

也就是说,我们在执行某段代码的时候,要拿到这个对象上的锁才可以进行,当有2个对象的时候,对这2个线程来说是可以并行的。也充分的说明锁的实例不一样,是可以并行的

类锁

类锁是作用于类的静态方法或者一个类的class对象上

public class DiffInstance {

    private static class A implements Runnable {
        private DiffInstance diffInstance;

        public A(DiffInstance diffInstance) {
            this.diffInstance = diffInstance;
        }

        @Override
        public void run() {
            System.out.println("A is running..." + diffInstance.toString());
            diffInstance.instanceA();
        }
    }

    private static class B implements Runnable {
        private DiffInstance diffInstance;

        public B(DiffInstance diffInstance) {
            this.diffInstance = diffInstance;
        }

        @Override
        public void run() {
            System.out.println("B is running..." + diffInstance);
            diffInstance.instanceB();
        }
    }
    
    //作用于类的静态方法
    private static synchronized void instanceA() {
        second(1);
        System.out.println("A is going...");
        second(1);
        System.out.println("A ended ");
    }
	
	//作用于类的class对象
    private synchronized void instanceB() {
        synchronized (DiffInstance.class){
           second(1);
           System.out.println("B is going..." + this.toString());
           second(1);
           System.out.println("B ended " + this.toString());
       }
    }

    /**
     * 按秒休眠
     * @param seconds 秒数
     */
    public static final void second(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
        }
    }

    public static void main(String[] args) {
        DiffInstance instance1 = new DiffInstance();
        Thread t1 = new Thread(new B(instance1));
        DiffInstance instance2 = new DiffInstance();
        Thread t2 = new Thread(new A(instance2));
        t1.start();
        t2.start();
    }
}

打印如下:

B is running...cn.wyhcsl.syn.DiffInstance@3abbf4ae
A is running...cn.wyhcsl.syn.DiffInstance@1667513b
B is going...cn.wyhcsl.syn.DiffInstance@3abbf4ae
B ended cn.wyhcsl.syn.DiffInstance@3abbf4ae
A is going...
A ended 

区别

实例锁和类锁是不同的,两者可以并行。代码如下:

/**
 *类说明:演示实例锁和类锁是不同的,两者可以并行
 */
public class InstanceAndClass {
	
    private static class SynClass extends Thread{
        @Override
        public void run() {
            System.out.println("TestClass is running...");
            synClass();
        }
    }

    private static class InstanceSyn implements Runnable{
        private InstanceAndClass SynClassAndInstance;

        public InstanceSyn(InstanceAndClass SynClassAndInstance) {
            this.SynClassAndInstance = SynClassAndInstance;
        }

        @Override
        public void run() {
            System.out.println("TestInstance is running..."+SynClassAndInstance);
            SynClassAndInstance.instance();
        }
    }

    private synchronized void instance(){
        SleepTools.second(1);
        System.out.println("synInstance is going..."+this.toString());
        SleepTools.second(1);
        System.out.println("synInstance ended "+this.toString());
    }

    private static synchronized void synClass(){
        SleepTools.second(1);
        System.out.println("synClass going...");
        SleepTools.second(1);
        System.out.println("synClass end");
    }

    public static void main(String[] args) {
        InstanceAndClass synClassAndInstance = new InstanceAndClass();
        Thread t1 = new SynClass();
        Thread t2 = new Thread(new InstanceSyn(synClassAndInstance));
        t2.start();
        SleepTools.second(1);
        t1.start();
    }
}

打印如下:

TestInstance is running...cn.enjoyedu.ch1.syn.InstanceAndClass@245c3ee2
synInstance is going...cn.enjoyedu.ch1.syn.InstanceAndClass@245c3ee2
TestClass is running...
synInstance ended cn.enjoyedu.ch1.syn.InstanceAndClass@245c3ee2
synClass going...
synClass end

synchronized作用于静态方法和非静态方法的区别:

  • 非静态方法:给对象加锁(可以理解为给这个对象的内存上锁,注意 只是这块内存,其他同类对象都会有各自的内存锁),这时候在其他一个以上线程中执行该对象的这个同步方法(注意:是该对象)就会产生互斥

  • 静态方法:相当于在类上加锁(*.class 位于代码区,静态方法位于静态区域,这个类产生的对象公用这个静态方法,所以这块内存,N个对象来竞争), 这时候,只要是这个类产生的对象,在调用这个静态方法时都会产生互斥

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪了个王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值