JAVA并发---记录2
个人记录:
一、没有同步的情况
package com.shob.syn; public class Syn { /** * synchronized是JAVA中解决并发问题的一种最常用的方法,也是最简单的方法。 作用有三个: 1、确保线程互斥的访问同步代码 * 2、保证共享变量的修改能够及时可见 3、有效解决重排序问题 用法: 1、修改普通方法 2、修饰静态方法 3、修饰代码块 */ private void desc() { } //没有同步的情况: public void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final Syn test = new Syn(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test.method2(); } }).start(); } }
二、对普通方法同步
package com.shob.syn; public class SynO { /** * 对普通方法同步: * 线程2需要等待线程1的method1执行完成才能开始执行method2方法。 */ public synchronized void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public synchronized void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } //由同一对象执行 public static void main(String[] args) { final SynO test = new SynO(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test.method2(); } }).start(); } }
三、静态方法(类)同步
package com.shob.syn; public class SynT { /** * 静态方法(类)同步 * 对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象, * 但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。 */ public static synchronized void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public static synchronized void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } //由不同对象执行,但是方法为静态方法属于类,而2个对象都是这个类的实例,相对静态方法需同步进行 public static void main(String[] args) { final SynT test = new SynT(); final SynT test2 = new SynT(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test2.method2(); } }).start(); } }
四、代码块同步
package com.shob.syn; public class SynS { /** * 代码块同步 * 虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。 */ public void method1() { System.out.println("Method 1 start"); try { synchronized (this) { System.out.println("Method 1 execute"); Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public void method2() { System.out.println("Method 2 start"); try { synchronized (this) { System.out.println("Method 2 execute"); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynS test = new SynS(); new Thread(new Runnable() { @Override public void run() { test.method1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test.method2(); } }).start(); } }
总:
package com.shob.syn; public class SynDemo { /** * * monitorenter: * 每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: 1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。 2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1. 3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。 */ private void method(){ //monitorenter synchronized (this) { System.out.println("Method 1 start"); } //monitorexit } /** * monitorexit * * 执行monitorexit的线程必须是objectref所对应的monitor的所有者。 指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor, 不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。 */ /** * 方法的同步方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现), * 不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。 * JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置, * 如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。 * 在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。 */ private synchronized void me(){ System.out.println("Method 2 start"); } }