synchronized
来源:互联网 发布:java pdf生成 编辑:程序博客网 时间:2024/06/15 19:12
1.synchronized同步方法
1.1方法内部私有变量为线程安全,实例变量非线程安全
public static void main(String[] args) { PrivateNum num = new PrivateNum(); MyThreadA a = new MyThreadA(num); MyThreadB b = new MyThreadB(num); a.start(); b.start(); } public static class PrivateNum { public void addnum(String name) { //重点是num变量是addnum这个方法的局部变量 int num = 0; try { if ("a".equals(name)) { num = 100; System.out.println("a ser over!"); Thread.sleep(2000); } else { num = 200; System.out.println("b set over!"); } System.out.println("线程" + name + "的num=" + num); } catch (Exception e) { e.printStackTrace(); } } } public static class MyThreadA extends Thread { private PrivateNum privateNum; public MyThreadA(PrivateNum privateNum) { super(); this.privateNum = privateNum; } @Override public void run() { super.run(); privateNum.addnum("a"); }} public static class MyThreadB extends Thread { private PrivateNum privateNum; public MyThreadB(PrivateNum privateNum) { super(); this.privateNum = privateNum; } @Override public void run() { super.run(); privateNum.addnum("b"); }}输出:
如果我们把num这个私有变量提到方法外边
public static class GetNum { //之所以会出现“非线程安全”的现象,是因为num这个变量是GetNUm类的全局变量 private int num = 0; public void getNum(String name) { try { if ("a".equals(name)) { num = 100; System.out.println("a set over"); 执行到这里的时候,线程a休眠了,此时num=100,接着线程b执行了,执行的结果把num重新赋值为num=200了,线程b执行完了之后,休眠的2000毫秒也过了, 线程a会接着执行,而不是重新执行,所以这个时候的num仍然是200,发生了“非线程安全问题” Thread.sleep(2000); } else { num = 200; System.out.println("b set over"); } System.out.println("线程" + name + "的num=" + num); } catch (Exception e) { e.printStackTrace(); } }}就会出现非线程安全问题:
为了防止线程安全问题,可以在方法前面加上synchronized关键字
public static class GetNum { //之所以会出现“非线程安全”的现象,是因为num这个变量是GetNUm类的全局变量 private int num = 0; synchronized public void getNum(String name) { try { if ("a".equals(name)) { num = 100; System.out.println("a set over"); 执行到这里的时候,线程a休眠了,此时num=100,接着线程b执行了,执行的结果把num重新赋值为num=200了,线程b执行完了之后,休眠的2000毫秒也过了, 线程a会接着执行,而不是重新执行,所以这个时候的num仍然是200,发生了“非线程安全问题” Thread.sleep(2000); } else { num = 200; System.out.println("b set over"); } System.out.println("线程" + name + "的num=" + num); } catch (Exception e) { e.printStackTrace(); } }}输出:
a set over线程a的num=100b set over 线程b的num=2001.2synchronized取得的都是对象锁
如果多个线程访问多个对象,JVM创建多个锁,运行顺序是可以交叉的
package Thread10;public class HasSelfPrivateNum { private int num=0; synchronized public void addI(String username){ try { if(username.equals("a")){ num=100; System.out.println("a结束"); Thread.sleep(2000);} else {num=200;System.out.println("b结束");} System.out.println(username+"num"+num); } catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }}package Thread10;public class ThreadA extends Thread { private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub super(); this.numRef=numRef; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("a");}}package Thread10;public class ThreadB extends Thread { private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub super(); this.numRef=numRef; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("b");}}package Thread10;public class Run { public static void main(String[] args) {HasSelfPrivateNum test1=new HasSelfPrivateNum();HasSelfPrivateNum test2=new HasSelfPrivateNum();ThreadA a=new ThreadA(test1);ThreadB b=new ThreadB(test1);a.start();b.start();}}
1.3A线程持有object对象的Lock锁,B线程可以调用object对象的非synchronized方法,但不可以调用object任何的synchronized方法。示例:
package Thread9;public class ThreadA extends Thread { private MyObject object; public ThreadA(MyObject object) {// TODO Auto-generated constructor stub super(); this.object=object; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run(); object.methodA(); }}package Thread9;public class ThreadB extends Thread { private MyObject object; public ThreadB(MyObject object) {// TODO Auto-generated constructor stub super(); this.object=object; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run(); object.methodB(); }}package Thread9;public class MyObject { synchronized public void methodA() { try { System.out.println("开始"+Thread.currentThread().getName());Thread.sleep(5000);System.out.println("结束a"+System.currentTimeMillis());} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} } synchronized public void methodB(){ // public void methodB(){ try { System.out.println("线程"+Thread.currentThread().getName()+System.currentTimeMillis());Thread.sleep(5000);System.out.println("B结束");} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }}package Thread9;public class Run { public static void main(String[] args) {MyObject test=new MyObject();ThreadA a=new ThreadA(test);a.setName("A");ThreadB b=new ThreadB(test);b.setName("B");a.start();b.start();}}当运行红色部分时,输出:
开始A结束a1507091234956线程B1507091234956B结束
此时线程B也没法访问methodB方法
当运行蓝色部分时,输出:开始A线程B1507091270428B结束结束a1507091275428methodB方法没有加synchronized所以可以在A线程持有锁时继续调用非synchronized方法。
1.4synchronized锁重入
可重入锁:自己可以再次获取自己的内部锁,在父子类继承环境中,子类是完全可以通过"可重入锁"调用父类的同步方法的。
在一个线程得到一个对象锁时,再次请求对象锁是可以再次得到该对象的锁的,举个简单的栗子吧:
package Thread11;public class Demo { synchronized public void service1(){ System.out.println("service1"); service2(); } synchronized public void service2(){ System.out.println("service2"); }}package Thread11;public class MyThread extends Thread { @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();Demo test=new Demo();test.service1();}}package Thread11;public class Run { public static void main(String[] args) {MyThread t=new MyThread();t.start();}}输出:
service1service21.5出现异常,锁自动释放
当一个线程执行的代码出现异常时,其持有的锁会自动释放。
代码:
package Thread10;public class HasSelfPrivateNum { private int num=0; synchronized public void addI(String username){ try { if(username.equals("a")){ System.out.println(100/0); Thread.sleep(2000);} else {num=200;System.out.println("b结束");} System.out.println(username+"num"+num); } catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();} }}package Thread10;public class ThreadA extends Thread { private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub super(); this.numRef=numRef; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("a");}}package Thread10;public class ThreadB extends Thread { private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub super(); this.numRef=numRef; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.addI("b");}}package Thread10;public class Run { public static void main(String[] args) {HasSelfPrivateNum test1=new HasSelfPrivateNum();HasSelfPrivateNum test2=new HasSelfPrivateNum();ThreadA a=new ThreadA(test1);ThreadB b=new ThreadB(test1);a.start();b.start();}}
输出:
b结束bnum200Exception in thread "Thread-0" java.lang.ArithmeticException: / by zeroat Thread10.HasSelfPrivateNum.addI(HasSelfPrivateNum.java:8)at Thread10.ThreadA.run(ThreadA.java:13)
1.6当子类重写父类方法的时候,如果不加入同步标志,一样不具备同步性。
如果父类 、子类中存在同样的方法。父类方法是同步的,子类是不同步的。那么子类的方法就不会继承父类的重载方法性质。必须自己加上synchronized关键字。
package Thread11;public class SynchronizationDoesNotHaveInheritance { public static void main(String[] args) throws InterruptedException { Sub sub = new Sub(); Father father = new Father(); ThreadOne threadOne = new ThreadOne(sub); Thread thread = new Thread(threadOne); thread.start(); ThreadTwo threadTwo = new ThreadTwo(sub); Thread thread2 = new Thread(threadTwo); thread2.start(); } } class ThreadOne implements Runnable { private Father father; public ThreadOne(Father sub) { this.father = sub; } @Override public void run() { father.service(); } } class ThreadTwo implements Runnable { private Father father; public ThreadTwo(Father father) { this.father = father; } @Override public void run() { father.service(); } } class Father { protected int count = 0; public synchronized void service() { for (int i = 0; i < 5; i++) { System.out.println("Thread name1:" + Thread.currentThread().getName() + " count:" + count++); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Sub extends Father { @Override public void service() { for (int i = 0; i < 5; i++) { System.out.println("Thread name2:" + Thread.currentThread().getName() + " count:" + count++); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
输出:Thread name2:Thread-0 count:0Thread name2:Thread-1 count:1Thread name2:Thread-0 count:3Thread name2:Thread-1 count:2Thread name2:Thread-1 count:4Thread name2:Thread-0 count:5Thread name2:Thread-0 count:6Thread name2:Thread-1 count:7Thread name2:Thread-1 count:8Thread name2:Thread-0 count:8
2.synchronized同步语句块2.1 synchronized(this)是锁定的当前对象
当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块。
package Thread12;public class SynObj { public synchronized void methodA() { System.out.println("methodA....."); System.out.println("A时间是"+System.currentTimeMillis()); try { Thread.sleep(5555); } catch (InterruptedException e) { e.printStackTrace(); } } public void methodB() { synchronized(this) { System.out.println("methodB....."); System.out.println("B时间是"+System.currentTimeMillis()); } } public void methodC() { String str = "sss"; //string容易引发问题,慎重使用 synchronized (str) { System.out.println("methodC....."); System.out.println("c时间是"+System.currentTimeMillis()); } }}package Thread12;public class TestSyn { public static void main(String[] args) { final SynObj obj = new SynObj(); Thread t1 = new Thread(new Runnable() { @Override public void run() { obj.methodA(); } }); t1.start(); Thread t2 = new Thread(new Runnable() { @Override public void run() { obj.methodB(); } }); t2.start(); Thread t3 = new Thread(new Runnable() { @Override public void run() { obj.methodC(); } }); t3.start(); }}输出:
methodA.....A时间是1507108745427methodC.....c时间是1507108745428methodB.....B时间是1507108750986根据输出可以看出:A和C时间相当,而B等A释放了锁才运行,说明synchronized和synchronized方法一样是锁对象的。
2.2将任意对象作为对象监视器
在多个线程持有"对象监视器"是同一个对象的前提下,同一时间只有一个线程可以执行synchronized(这个对象)中的代码,如果对象监视器不是同一个对象,就会交叉执行。
package Thread13;public class ThreadA extends Thread { private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub super(); this.numRef=numRef; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.methodA();}}package Thread13;public class ThreadB extends Thread { private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef) {// TODO Auto-generated constructor stub super(); this.numRef=numRef; } @Overridepublic void run() {// TODO Auto-generated method stubsuper.run();numRef.methodB();}}package Thread13;import org.omg.CORBA.PRIVATE_MEMBER;public class HasSelfPrivateNum { private String test =new String(); private String test1 =new String(); public void methodA(){ try { synchronized (test) { System.out.println("a开始");Thread.sleep(1000);System.out.println("a结束"); }} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}} public void methodB(){ try { synchronized (test) { //synchronized (test1) { System.out.println("b开始");Thread.sleep(1000);System.out.println("b结束"); }} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}package Thread13;public class Run { public static void main(String[] args) {HasSelfPrivateNum test=new HasSelfPrivateNum();ThreadA a=new ThreadA(test);ThreadB b=new ThreadB(test);a.start();b.start();}}如果我们保留蓝色部分,说明是同一个对象,此时输出:
b开始b结束a开始a结束我们保留红色部分时,不是同一个对象,交替运行:
a开始b开始a结束b结束
0 0
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- synchronized
- 首次在远程主机中部署WEB应用
- 题目1146:Flipping Pancake
- 在MyEclipse中上传项目到github的步骤(很详细)
- 数据结构(1):线性表
- 【寒江雪】测试环境
- synchronized
- webservices系列(六)——spring整合Axis2
- [资源] 开发笔记
- 《收获,不止Oracle》读书笔记(二):表连接
- 大数据学习——HBase 入门
- LeetCode079 Word Search
- 设计模式——代理模式
- lammps 编译安装测试说明 (提供免费测试)
- 4.19