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

    技术2022-07-12  87

    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个对象来竞争), 这时候,只要是这个类产生的对象,在调用这个静态方法时都会产生互斥

    Processed: 0.018, SQL: 9