java synchronized关键字

来源:互联网 发布:百度云域名注册免费 编辑:程序博客网 时间:2024/06/09 13:40

synchronized关键字用于多线程同步,其具有以下作用:
1. 确保线程互斥的访问同步代码
2. 保证共享变量的修改能够及时可见
3. 有效解决重排序问题

其主要有三种用法:
1. 修饰普通方法
2. 修饰静态方法
3. 修饰代码块

测试代码:

public class C{    private static ArrayList<Integer> list=new ArrayList<>();    public void noSyn() {        for(int i=0;i<5;++i){            System.out.println(Thread.currentThread().getName()+" "+"no Syn");            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }        }    }    public synchronized void normalSyn1() {        for(int i=0;i<5;++i){            System.out.println(Thread.currentThread().getName()+" "+"normal Syn1 ");            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }        }    }    public static void noSynStatic() {        for(int i=0;i<5;++i){            System.out.println(Thread.currentThread().getName()+" "+"no Syn static");            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }        }    }    public static synchronized void staticSyn() {        for(int i=0;i<5;++i){            //list.add(i);            System.out.println(Thread.currentThread().getName()+" "+"static syn "+list);            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }        }    }    public void staticString1(){        synchronized (list) {            for(int i=0;i<5;++i){                list=new ArrayList<>();                System.out.println(Thread.currentThread().getName()+" "+"static integer1 "+list);                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    // TODO 自动生成的 catch 块                    e.printStackTrace();                }            }        }    }    public void staticString2(){        synchronized (list) {            for(int i=0;i<5;++i){                System.out.println(Thread.currentThread().getName()+" "+"static integer2 "+list);                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    // TODO 自动生成的 catch 块                    e.printStackTrace();                }            }        }    }    public synchronized void normalSyn2() {        for(int i=0;i<5;++i){            System.out.println(Thread.currentThread().getName()+" "+"normal Syn2");            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }        }    }}

其特点如下:
1.synchronized修饰普通方法methodA时,在不同线程中使用同一对象调用方法methodA,这样会导致先获取该对象锁的线程先执行,其他线程受阻塞。

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.normalSyn();              }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c.normalSyn();            }        }).start();    }

这里写图片描述

2.synchronized修饰普通方法methodA、methodB时,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致先获取对象锁的线程先执行,其他线程受阻塞。这表明,synchronized修饰普通方式时,锁住的是对象。当某一个线程获取到该对象锁时,其他线程就不能执行需要该对象锁的方法(即synchronized修饰的普通方法,代码块上为this的synchronized代码块)

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.normalSyn1();             }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c.normalSyn2();            }        }).start();    }

结果:
这里写图片描述

3.synchronized修饰普通方法methodA时,在不同线程中使用不同对象调用该方法,这样会导致各线程按CPU调度进行,没有发送阻塞。这表明,不同对象有不同的对象锁,互不影响。

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.normalSyn1();             }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c1.normalSyn1();            }        }).start();

结果:
这里写图片描述

4.synchronized修饰普通方法methodA时,在不同线程中同一对象,在线程A中调用methodA,在线程B中调用非synchronized的方法methodB,这样会导致各线程按CPU调度进行,没有发送阻塞。这表明,没有锁的方法不会受到对象锁影响。

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.normalSyn1();             }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c.noSyn();            }        }).start();    }

结果:
这里写图片描述

5.synchronized修饰静态方法时,在不同线程中使用该类的不同对象调用该方法时,这样会导致先获取类锁的线程先执行,其他线程阻塞。

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.staticSyn();            }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c1.staticSyn();            }        }).start();    }

结果:
这里写图片描述

6.synchronized修饰普通方法methodA、静态方法methodB时,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致线程按CPU调度进行,各不影响。因为普通方法锁的是对象,静态方法锁的是类,两者互不干扰。

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.staticSyn();            }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c.normalSyn1();            }        }).start();    }

结果:
这里写图片描述

7.在方法methodA中用synchronized修饰代码块上的静态变量,synchronized修饰静态方法methodB,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致线程按CPU调度进行,各不影响。这表明虽然synchronized修饰的是静态变量,但它锁的是该变量;而synchronized修饰静态方式锁的是类,这样它们还是会互不影响。所以在synchronized锁变量时,必须要注意其在其他方法中的控制。
C类中方法staticSyn有所改变:

public static synchronized void staticSyn() {        for(int i=0;i<5;++i){            System.out.println(Thread.currentThread().getName()+" "+"static syn "+list);            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                // TODO 自动生成的 catch 块                e.printStackTrace();            }        }    }
public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.staticString2();            }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c.staticSyn();            }        }).start();    }

结果:
这里写图片描述

8.在方法methodA中用synchronized修饰代码块上的静态变量,在方法methodB中用synchronized修饰代码块上的同样的静态变量,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致先获取类锁的线程先执行,其他线程阻塞。前提是在先执行的线程中没有改变该静态变量的内存地址。若改变了,那么线程会按CPU调度运行。因为synchronized锁的是变量的那个内存位置,若后面改变了该变量的内存位置,在其他方法中比对当前位置与被锁位置不一致,则会执行。

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.staticString1();                  }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c.staticString2();            }        }).start();    }

结果:
这里写图片描述

顺序改变一下:(先执行的线程没有改变地址)

public static void main(String []ags) throws Exception{        final C c=new C();        final C c1=new C();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                c.staticString2();                      }        }).start();        new Thread(new Runnable() {                     @Override            public void run() {                // TODO 自动生成的方法存根                       c.staticString1();            }        }).start();    }

结果:
这里写图片描述

原创粉丝点击