synchronized的几种用法

java中,当我们处理线程同步问题的时候就会用到synchronized这个关键字,下面我们介绍下synchronized的几种用法,介绍之前我们先来看下,在java 多线程中 如果没有线程同步会出现什么问题:
下面这个是一个测试例子:


public class MainClass {

    public static class MyRun implements Runnable
    {

        private int count=0;


        @Override
        public void run() {
            while (count<15)
            {


                    System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                    count++;
				try {
                        Thread.sleep(200);
                    }
                    catch (Exception e)
                    {

                    }
            }


        }
    }


    public  static void main(String args[])
    {

        MyRun myRun=new MyRun();

        Thread threadA=new Thread(myRun,"A");
        Thread threadB=new Thread(myRun,"B");
        threadA.start();
        threadB.start();

    }
}

运行结果:

ThreadName:A  count:0
ThreadName:B  count:0
ThreadName:A  count:1
ThreadName:A  count:3
ThreadName:B  count:2
ThreadName:A  count:4
ThreadName:A  count:6
ThreadName:A  count:7
ThreadName:B  count:5
ThreadName:A  count:8
ThreadName:A  count:10
ThreadName:A  count:11
ThreadName:A  count:12
ThreadName:B  count:9
ThreadName:A  count:13
ThreadName:B  count:14

我们看到这个count在无序的增加,这个是由于A,B两个线程同时操作Count变量造成的,如果我们想让Count有序增加,应该给

   System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                    count++;

这段代码同步枷锁,这样当B线程进入这里时候,发现这里已经被锁,就只有等待,A执行完这段代码之后就会释放对这个对象的这段代码的释放锁,A获得了释放锁,就可以进入执行,让A,B有序进入执行,才能让Count有序增加,加入了synchronized之后的代码:

 while (count<15)
            {

                    synchronized (this)
                    {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }
                    try {
                        Thread.sleep(200);
                    }
                    catch (Exception e)
                    {

                    }


            }

结果:

ThreadName:A  count:0
ThreadName:B  count:1
ThreadName:B  count:2
ThreadName:A  count:3
ThreadName:A  count:4
ThreadName:B  count:5
ThreadName:B  count:6
ThreadName:A  count:7
ThreadName:B  count:8
ThreadName:A  count:9
ThreadName:A  count:10
ThreadName:B  count:11
ThreadName:B  count:12
ThreadName:A  count:13
ThreadName:B  count:14
ThreadName:A  count:15

加了锁之后A,B线程就可以有序的交替执行,不会同时抢占执行Count++ 操作,
下面介绍synchronised的几种用法

1、锁方法(1)

public synchronized void dodo()
        {

        }

这个就是锁方法,这里面要注意两点:

1、synchronized 关键子不是方法的一部分,所以它不会被继承,说白了,就是如果父类的方法有synchronized,子类重写这个方法,synchronized不写也不会报错

2、synchronized 不能修饰接口

3、synchronized 不能修复构造方法,但是可以在构造方法里面的代码块

2、锁代码块

锁代码块就是我上面的那个例子写法了,这个就是锁的是某个对象中的某个代码,让它线程同步

 synchronized (this)
                    {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }

我们看到这个synchronized (this) 里面的this,这里是要传入一个对象的,如果不用this也可以,也可以在这个类里面new一个其他对象效果也是一样的,比如

        private Object obj=new Object();
        @Override
        public void run() {
            while (count<15)
            {

                    synchronized (obj)
                    {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }
                    try {
                        Thread.sleep(200);
                    }
                    catch (Exception e)
                    {

                    }


            }


        }

这里就用了obj这个new的对象,效果和this完全一样

3、锁某个类

public class MainClass {

    public static class MyRun implements Runnable
    {

        public static int count=0;


        @Override
        public void run() {
            while (count<15)
            {

                    synchronized (this)
                    {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }
                    try {
                        Thread.sleep(200);
                    }
                    catch (Exception e)
                    {

                    }


            }


        }
        public synchronized void dodo()
        {

        }
    }


    public  static void main(String args[])
    {

        MyRun myRun1=new MyRun();
        MyRun myRun2=new MyRun();
        Thread threadA=new Thread(myRun1,"A");
        Thread threadB=new Thread(myRun2,"B");
        threadA.start();
        threadB.start();

    }
}

这个代码里面,

 MyRun myRun1=new MyRun();
        MyRun myRun2=new MyRun();

我new两个MyRun,我前面说过synchronized(this)只能锁某个对象,就是说threadA执行myRun1 threadB执行myRun2,互不干扰,synchronized只能锁自己的run1 或者run2 不能两个对象同时锁到,所以执行的结果是无序的

ThreadName:A  count:0
ThreadName:B  count:0
ThreadName:A  count:1
ThreadName:B  count:1
ThreadName:B  count:2
ThreadName:A  count:2
ThreadName:B  count:3
ThreadName:A  count:3
ThreadName:B  count:4
ThreadName:A  count:4
ThreadName:B  count:5
ThreadName:A  count:5
ThreadName:A  count:6
ThreadName:B  count:6
ThreadName:A  count:7
ThreadName:B  count:7
ThreadName:A  count:8
ThreadName:B  count:8
ThreadName:B  count:9
ThreadName:A  count:9
ThreadName:A  count:10
ThreadName:B  count:10
ThreadName:A  count:11
ThreadName:B  count:11
ThreadName:A  count:12
ThreadName:B  count:12
ThreadName:B  count:13
ThreadName:A  count:13
ThreadName:B  count:14
ThreadName:A  count:14

如果我们想让两个线程有序执行,这个Count++操作,而且对run1,和run2都同时锁,应该怎么办呢???
答案是锁类,锁类的意思是不管是这个类new了多少对象,这个对象的所有方法,都会上锁,我们更改下代码看看结果,怎么锁类的:


                    synchronized (MyRun.class)
                    {
                        System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                        count++;
                    }

我们看到只改动了 synchronized (MyRun.class)这里,其他代码都不变们,这个就把这个MyRun锁了,我们看看结果:

ThreadName:A  count:0
ThreadName:B  count:1
ThreadName:B  count:2
ThreadName:A  count:3
ThreadName:B  count:4
ThreadName:A  count:5
ThreadName:A  count:6
ThreadName:B  count:7
ThreadName:A  count:8
ThreadName:B  count:9
ThreadName:A  count:10
ThreadName:B  count:11
ThreadName:B  count:12
ThreadName:A  count:13
ThreadName:A  count:14
ThreadName:B  count:15

结果是有序的,验证了我们的结果

4、静态方法上的synchronized

public synchronized  static void ddo()

这种方式其实和第三种效果是一样的,都是对类起作用,因为我们知道static修饰的方法是类方法,所以这个synchronized 也是作用于整个类
我们把上面的代码改下看看效果是不是一样:

public class MainClass {

    public static class MyRun implements Runnable
    {

        public static int count=0;


        @Override
        public void run() {

            ddo();

        }

        public synchronized  static void ddo()
        {
            while (count<15)
            {



                    System.out.print("ThreadName:"+Thread.currentThread().getName()+"  count:"+count+"\n");
                    count++;

                try {
                    Thread.sleep(200);
                }
                catch (Exception e)
                {

                }


            }
        }


    }


    public  static void main(String args[])
    {

        MyRun myRun1=new MyRun();
        MyRun myRun2=new MyRun();
        Thread threadA=new Thread(myRun1,"A");
        Thread threadB=new Thread(myRun2,"B");
        threadA.start();
        threadB.start();

    }
}

结果:

ThreadName:A  count:0
ThreadName:A  count:1
ThreadName:A  count:2
ThreadName:A  count:3
ThreadName:A  count:4
ThreadName:A  count:5
ThreadName:A  count:6
ThreadName:A  count:7
ThreadName:A  count:8
ThreadName:A  count:9
ThreadName:A  count:10
ThreadName:A  count:11
ThreadName:A  count:12
ThreadName:A  count:13
ThreadName:A  count:14

因为A线程 先执行最后满足条件 while (count<15),所以B没有机会执行了,验证符合我们的预期

总结

1、锁如果加在方法上面,或者在方法中的代码块形式,就是锁的这个对象,如果锁是静态方法中,或者代码块synchronized(A.class) 形式 就是锁的这个类,里面的所有方法都会同步
2、谁拥有了锁,上面线程就拥有了控制这段代码的能力,其他的线程只能看着,可以联想到生活中的真的锁,只有释放了锁,其他线程才可以操作
3、synchronized 很好消耗系统性能,所以能不加锁的逻辑,尽量不要加,
4、操作读写文件,或者数据库,有的时候多线程会出现不可预知的问题,所以要加入锁

猜你喜欢

转载自blog.csdn.net/fagawee/article/details/103258226