synchronize我的理解是为了保证程序中的原子性和一致性,即当你有两个线程同时操作一段代码的时候,要让这段代码的执行是在任何状态下都是正确的,首先要保证synchronize的使用要对同一个对象和同一把锁使用。
[java] view plain copy
- <span style="font-size:14px;">public class TraditionalThreadSynchronized {
- public static void main(String[] args) {
- TraditionalThreadSynchronized test =new TraditionalThreadSynchronized();
- test.init();//创建同一个对象
- }
- private void init(){
- OutPuter outPuter =new OutPuter();
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while(true){
- Thread.sleep(10);
- outPuter.outer("abcdefg");
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }).start();//线程1
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while(true){
- Thread.sleep(10);
- outPuter.outer("123456789");
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }).start();//线程2
- }
- class OutPuter {
- public synchronized void outer(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }</span>
我们想要打印的结果是abcdefg和123456789交替打印输出,但是输出结果却是下图
我们发现打印的结果是两个String交替打印出来的,所以这个线程现在是不安全的,那么此时我们就需要使用synchronize保持两个线程的原子性,下面我们把上面程序中的OutPut方法加上synchronize锁试一下
[java] view plain copy
- <span style="font-size:14px;"> class OutPuter {
- public void outer(String name) {
- synchronized (name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }</span>
我们发现加了synchronize关键字的锁以后还是会出现这种线程不安全的情况,那是为什么呢?原因就出在这个锁name上,因为两个线程中的name一个是abcdefg一个是123456789,两个锁根本不是一个锁,所以线程也是不安全的,那么我们再把上面的方法修改一下
[java] view plain copy
- <span style="font-size:14px;">class OutPuter {
- public void outer(String name) {
- synchronized (this) {//或者创建一个新的锁把,this换成锁也可以,此处的this指的是outer这个对象
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- }</span>
我们发现此时,上面的程序运行不会出现线程安全问题。
我们此时把上面的syn加在outer上面也是可以的
[java] view plain copy
- class OutPuter {
- public synchronized void outer(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
这样也是可以实现线程安全的,但是一般情况下synchronize在一段代码中只要使用一次就够了,因为多个synchronize可能会出现死锁问题!
此时我们把上面的OutPuter类修改一下
[java] view plain copy
- class OutPuter {
- public void outer(String name) {
- synchronized (this) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- }
- public synchronized void outer2(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- }
- span style="font-size:14px;"> }</span>
然后把上面的outPuter.outer(“123456789”)改成outPuter.outer2(“123456789”)
此时还能保证线程安全么,也就是说output和output2会保持互斥么?当然也是可以的,因为此时的两个锁代表的也是同一把锁,都是代表当前的对象,而当前对象此时是一个对象,所以是可以实现我们想要的效果的。
那么此时我们把上面的
然后把上面的outPuter.outer(“123456789”)改成new outPuter().outer(“123456789”)
我们会发现依然会出现线程安全问题,那是为什么呢 ?那是因为我们每次new的OutPuter()并不是同一个对象。所以说明synchronize既要保证是同一个对象,又要保证是同一把锁。
此时我们再加一个output3方法
[java] view plain copy
- public static synchronized void outer3(String name) {
- for(int i = 0;i<name.length();i++){
- System.out.print(name.charAt(i));
- }
- System.out.println();
- span style="font-size:14px;"> }</span>
注意这个output3方法是静态的,那么我们需要把OutPuter类也改成静态类
然后把上面的outPuter.outer(“123456789”)改成outPuter.outer3(“123456789”)
答案是不行的,因为此时我们使用的output3中的锁是类锁,而output中的锁是当前对象锁,根本不是一把锁,那么此时我们要是想让output和output3同步需要怎么做呢?我们把output中的this改成OutPuter.class就可以实现同步了。此时我们两个方法的锁就都是类锁了,即为同一把锁。
线程还在学习中,学习是痛苦的,但是我希望能真正的坚持做一件事情!