南京网站建设一条龙,汶上网站制作,网页设计培训 多少钱,湖北建设企业网站价格synchronized是Java中的关键字#xff0c;是一种同步锁。它修饰的对象有以下几种#xff1a;
修饰一个方法
被修饰的方法称为同步方法#xff0c;其作用的范围是整个方法#xff0c;作用的对象是调用这个方法的对象#xff1b;
修饰一个静态的方法
其作用的范围是整个…synchronized是Java中的关键字是一种同步锁。它修饰的对象有以下几种
修饰一个方法
被修饰的方法称为同步方法其作用的范围是整个方法作用的对象是调用这个方法的对象
修饰一个静态的方法
其作用的范围是整个静态方法作用的对象是这个类的所有对象
修饰一个代码块
被修饰的代码块称为同步语句块其作用的范围是大括号{}括起来的代码作用的对象是调用这个代码块的对象
修饰一个类
其作用的范围是synchronized后面括号括起来的部分作用主的对象是这个类的所有对象。 修饰一个方法
被修饰的方法称为同步方法其作用的范围是整个方法作用的对象是调用这个方法的对象
如果多个线程访问同一个对象的实例变量可能出现非线程安全问题。 例子a线程set后sleep2000ms看b线程是否可以趁机set造成非线程安全
HasSelfPrivateNum.java:
package service;public class HasSelfPrivateNum {private int num 0;public void addI(String username) {try {if (username.equals(a)) {num 100;System.out.println(a set over!);Thread.sleep(2000);} else {num 200;System.out.println(b set over!);}System.out.println(username num num);} catch (InterruptedException e) {e.printStackTrace();}}}A:
package extthread;import service.HasSelfPrivateNum;public class ThreadA extends Thread {private HasSelfPrivateNum numRef;public ThreadA(HasSelfPrivateNum numRef) {super();this.numRef numRef;}Overridepublic void run() {super.run();numRef.addI(a);}}B:
package extthread;import service.HasSelfPrivateNum;public class ThreadB extends Thread {private HasSelfPrivateNum numRef;public ThreadB(HasSelfPrivateNum numRef) {super();this.numRef numRef;}Overridepublic void run() {super.run();numRef.addI(b);}}run
package test;import service.HasSelfPrivateNum;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {HasSelfPrivateNum numRef new HasSelfPrivateNum();ThreadA athread new ThreadA(numRef);athread.start();ThreadB bthread new ThreadB(numRef);bthread.start();}
}结果a线程set后sleep2000msb线程可以趁机set造成非线程安全 这时我们使用synchronized修饰addI方法
package service;public class HasSelfPrivateNum {private int num 0;synchronized public void addI(String username) {try {if (username.equals(a)) {num 100;System.out.println(a set over!);Thread.sleep(2000);} else {num 200;System.out.println(b set over!);}System.out.println(username num num);} catch (InterruptedException e) {e.printStackTrace();}}}结果B不能set了说明线程安全。 注意我们取得的是对象锁也就是说一个对象一个锁而不是锁住整个类或者代码或者方法。
例子两个对象两个锁
myobject.java
打印名字后sleep最后打印end
package extobject;public class MyObject {synchronized public void methodA() {try {System.out.println(begin methodA threadName Thread.currentThread().getName());Thread.sleep(5000);System.out.println(end);} catch (InterruptedException e) {e.printStackTrace();}}}A:
package extthread;import extobject.MyObject;public class ThreadA extends Thread {private MyObject object;public ThreadA(MyObject object) {super();this.object object;}Overridepublic void run() {super.run();object.methodA();}}B:
package extthread;import extobject.MyObject;public class ThreadB extends Thread {private MyObject object;public ThreadB(MyObject object) {super();this.object object;}Overridepublic void run() {super.run();object.methodA();}
}RUN:
package test.run;import extobject.MyObject;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {MyObject object new MyObject();ThreadA a new ThreadA(object);a.setName(A);ThreadB b new ThreadB(object);b.setName(B);a.start();b.start();}}结果两个对象互不影响各自运行各自上锁 其它方法被调用是什么效果呢
之前做实验是因为怕大家不理解知识现在大家已经有了一定的基础这个结论不再做实验。
结论
如果A线程持有x对象的锁B线程不可调用synchronized修饰的方法但是可以异步调用没有被synchronized修饰的方法 synchronized具有锁重入功能也就是说一个线程获得锁再次请求是可以再次得到对象的锁的
下面做实验验证这个结论
service.java
package myservice;public class Service {synchronized public void service1() {System.out.println(service1);service2();}synchronized public void service2() {System.out.println(service2);service3();}synchronized public void service3() {System.out.println(service3);}}thread:
package extthread;import myservice.Service;public class MyThread extends Thread {Overridepublic void run() {Service service new Service();service.service1();}}run:
package test;import extthread.MyThread;public class Run {public static void main(String[] args) {MyThread t new MyThread();t.start();}
}结果验证了上面的结论
注在父子类之间同样适用不再做实验 但是如果一个线程出现了异常难道就永远锁住了资源吗其实不是的出现异常自动释放锁。
实验让a锁住对象后出现异常看b是否可以拿到锁代码不再展示。 结果b可以正常执行。 修饰一个静态的方法 其作用的范围是整个静态方法作用的对象是这个类的所有对象
也就是说整个类就一个锁这也和静态的概念相符静态方法和属性是属于类的而不是具体一个对象的
让我们来验证这个结论
service
package service;public class Service {synchronized public static void printA() {try {System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 进入printA);Thread.sleep(3000);System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 离开printA);} catch (InterruptedException e) {e.printStackTrace();}}synchronized public static void printB() {System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 进入printB);System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 离开printB);}}a
package extthread;import service.Service;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service service;}Overridepublic void run() {service.printA();}
}b
package extthread;import service.Service;public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.service service;}Overridepublic void run() {service.printB();}
}run
package test;import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {Service service1 new Service();Service service2 new Service();ThreadA a new ThreadA(service1);a.setName(A);a.start();ThreadB b new ThreadB(service2);b.setName(B);b.start();}}结果 但是请注意一个显而易见的结论a线程访问synchronized修饰的静态方法时b线程可以访问synchronized修饰的非静态方法原因也很容易想到静态方法是属于类的普通方法是属于对象本身的所以一个是对象锁一个是class锁不会影响。
为了验证这个结论我们做实验看看结果。
service
AB为静态的C普通的。
package service;public class Service {synchronized public static void printA() {try {System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 进入printA);Thread.sleep(3000);System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 离开printA);} catch (InterruptedException e) {e.printStackTrace();}}synchronized public static void printB() {System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 进入printB);System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 离开printB);}synchronized public void printC() {System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 进入printC);System.out.println(线程名称为 Thread.currentThread().getName() 在 System.currentTimeMillis() 离开printC);}}a
package extthread;import service.Service;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service service;}Overridepublic void run() {service.printA();}}b
package extthread;import service.Service;public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.service service;}Overridepublic void run() {service.printB();}
}c
package extthread;import service.Service;public class ThreadC extends Thread {private Service service;public ThreadC(Service service) {super();this.service service;}Overridepublic void run() {service.printC();}
}run
package test;import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;
import extthread.ThreadC;public class Run {public static void main(String[] args) {Service service new Service();ThreadA a new ThreadA(service);a.setName(A);a.start();ThreadB b new ThreadB(service);b.setName(B);b.start();ThreadC c new ThreadC(service);c.setName(C);c.start();}}结果 说明验证了结论因为AB同步执行C异步执行。 修饰一个代码块
被修饰的代码块称为同步语句块其作用的范围是大括号{}括起来的代码作用的对象是调用这个代码块的对象而不一定是本对象了
synchronized方法是对当前对象加锁synchronized代码块是对某个对象加锁
用synchronized声明方法是有弊端的比如A调用同步方法执行一个很长时间的任务这时B就必须等待有时候这种长时间等待是低效率且没有必要的这时我们就要认识一下synchronized同步代码块了。
格式是这样的synchronized(类名){......}
我们先来认识第一种用法来体会修饰代码块的好处
synchronized(this)同步代码块a调用相关代码后b对其它synchronized方法和synchronized(this)同步代码块调用会阻塞。但是没有被synchronized修饰的代码就得以执行不像之前修饰方法那么死板了。
我们来看一个例子
第一个循环异步第二个循环同步
package mytask;public class Task {public void doLongTimeTask() {for (int i 0; i 100; i) {System.out.println(nosynchronized threadName Thread.currentThread().getName() i (i 1));}System.out.println();synchronized (this) {for (int i 0; i 100; i) {System.out.println(synchronized threadName Thread.currentThread().getName() i (i 1));}}}
}thread1
package mythread;import mytask.Task;public class MyThread1 extends Thread {private Task task;public MyThread1(Task task) {super();this.task task;}Overridepublic void run() {super.run();task.doLongTimeTask();}}thread2
package mythread;import mytask.Task;public class MyThread2 extends Thread {private Task task;public MyThread2(Task task) {super();this.task task;}Overridepublic void run() {super.run();task.doLongTimeTask();}}run
package test;import mytask.Task;
import mythread.MyThread1;
import mythread.MyThread2;public class Run {public static void main(String[] args) {Task task new Task();MyThread1 thread1 new MyThread1(task);thread1.start();MyThread2 thread2 new MyThread2(task);thread2.start();}
}结果
在非同步代码块时是交叉打印的 同步代码块时排队执行 另一个用法
synchronized(非this)同步代码块也就是说将任意对象作为对象监视器
格式synchronized(非this对象x){......}
1、当多个线程持有的对象监听器为同一个对象时依旧是同步的同一时间只有一个可以访问
2、但是对象不同执行就是异步的。
这样有什么好处呢
因为如果一个类有很多synchronized方法或synchronizedthis代码块还是会影响效率这时用synchronized(非this)同步代码块就不会和其它锁this同步方法争抢this锁
实验
第一点我们就不验证了因为体现不出它的好处我们验证第二点
service
一个同步非this代码块一个同步方法
package service;public class Service {private String anyString new String();public void a() {try {synchronized (anyString) {System.out.println(a begin);Thread.sleep(3000);System.out.println(a end);}} catch (InterruptedException e) {e.printStackTrace();}}synchronized public void b() {System.out.println(b begin);System.out.println(b end);}}a
package extthread;import service.Service;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service service;}Overridepublic void run() {service.a();}}b
package extthread;import service.Service;public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.service service;}Overridepublic void run() {service.b();}}run
package test;import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {Service service new Service();ThreadA a new ThreadA(service);a.setName(A);a.start();ThreadB b new ThreadB(service);b.setName(B);b.start();}}结果 确实是异步的。
最后一个要注意的点我们知道synchronized(非this对象x){......}是将对象x监视这也就意味着当线程a调用这段代码时线程b调用类x中的同步方法和代码块也会是同步的效果阻塞。
为了让大家更明白做最后一个例子
首先创建一个有静态方法的类
package test2.extobject;public class MyObject {synchronized public void speedPrintString() {System.out.println(speedPrintString ____getLock time System.currentTimeMillis() run ThreadName Thread.currentThread().getName());System.out.println(-----------------);System.out.println(speedPrintString releaseLock time System.currentTimeMillis() run ThreadName Thread.currentThread().getName());}
}然后用 synchronized(非this对象x){......}的形式引用它并进入这个代码块然后看看这时这个静态方法是否可以被调用。
service作用是synchronized(object)
package test2.service;import test2.extobject.MyObject;public class Service {public void testMethod1(MyObject object) {synchronized (object) {try {System.out.println(testMethod1 ____getLock time System.currentTimeMillis() run ThreadName Thread.currentThread().getName());Thread.sleep(5000);System.out.println(testMethod1 releaseLock time System.currentTimeMillis() run ThreadName Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}}}threadA
package test2.extthread;import test2.extobject.MyObject;
import test2.service.Service;public class ThreadA extends Thread {private Service service;private MyObject object;public ThreadA(Service service, MyObject object) {super();this.service service;this.object object;}Overridepublic void run() {super.run();service.testMethod1(object);}}threadB
package test2.extthread;import test2.extobject.MyObject;public class ThreadB extends Thread {private MyObject object;public ThreadB(MyObject object) {super();this.object object;}Overridepublic void run() {super.run();object.speedPrintString();}
}run package test2.run;import test2.extobject.MyObject;
import test2.extthread.ThreadA;
import test2.extthread.ThreadB;
import test2.service.Service;public class Run {public static void main(String[] args) throws InterruptedException {Service service new Service();MyObject object new MyObject();ThreadA a new ThreadA(service, object);a.setName(a);a.start();Thread.sleep(100);ThreadB b new ThreadB(object);b.setName(b);b.start();}}结果 是同步的。
当然把修饰方法改为修饰代码块也是一样不能被执行的。
Synchronized的作用主要有三个1确保线程互斥的访问同步代码2保证共享变量的修改能够及时可见3有效解决重排序问题。
synchronized底层原理
在 Java 早期版本中synchronized属于重量级锁效率低下因为监视器锁monitor是依赖于底层的操作系统来实现Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程都需要操作系统帮忙完成而操作系统实现线程之间的切换时需要从用户态转换到内核态这个状态之间的转换需要相对比较长的时间时间成本相对较高。
在 Java 6 之后从 JVM 层面对synchronized 较大优化锁的实现引入了如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。 synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令。
其中 monitorenter 指令指向同步代码块的开始位置monitorexit 指令则指明同步代码块的结束位置。
当执行 monitorenter 指令时线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象的对象头中synchronized 锁便是通过这种方式获取锁的也是为什么Java中任意对象可以作为锁的原因) 的持有权.当计数器为0则可以成功获取获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后将锁计数器设为0表明锁被释放。如果获取对象锁失败那当前线程就要阻塞等待直到锁被另外一个线程释放为止。
synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令取得代之的是ACC_SYNCHRONIZED标识该标识指明了该方法是一个同步方法JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法从而执行相应的同步调用。但是原理其实都是类似的。具体的实现是操作系统的知识可以去翻我操作系统的文章。
锁详解
锁主要存在四种状态依次是无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级这种策略是为了提高获得锁和释放锁的效率。
自旋当有个线程A去请求某个锁的时候这个锁正在被其它线程占用但是线程A并不会马上进入阻塞状态而是循环请求锁(自旋)。这样做的目的是因为很多时候持有锁的线程会很快释放锁的线程A可以尝试一直请求锁没必要被挂起放弃CPU时间片因为线程被挂起然后到唤醒这个过程开销很大,当然如果线程A自旋指定的时间还没有获得锁仍然会被挂起。
自适应性自旋自适应性自旋是自旋的升级、优化自旋的时间不再固定而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态决定。例如线程如果自旋成功了那么下次自旋的次数会增多因为JVM认为既然上次成功了那么这次自旋也很有可能成功那么它会允许自旋的次数更多。
锁消除是指虚拟机即时编译器在运行时对一些代码上要求同步但是被检测到不可能存在共享数据竞争的锁进行消除。
偏向锁的目的是消除数据在无竞争情况下的同步原语进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量那么偏向锁就是在无竞争的情况下把整个同步都消除掉连CAS操作都不用做了。偏向锁默认是开启的也可以关闭。