Java并发编程:synchronized

来源:互联网 发布:越前南次郎的实力数据 编辑:程序博客网 时间:2024/06/10 00:29

当多个线程同时访问临界资源(一个对象,对象中的属性,一个文件,一个数据库等)时,就可能会产生线程安全问题。
不过,当多个线程执行一个方法,方法内部的局部变量并不是临界资源,因为方法是在栈上执行的,而Java栈是线程私有的,因此不会产生线程安全问题。

在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。
在Java中,可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。

当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
关键字synchronized拥有锁重入的功能。所谓锁重入的意思就是:当一个线程得到一个对象锁后,再次请求此对象锁时时可以再次得到该对象的锁的。这种锁重入的机制,也支持在父子类继承的环境中。

synchronized方法

import java.util.ArrayList;public class Test {    public static void main(String[] args)  {        final InsertData insertData = new InsertData();        new Thread() {            public void run() {                insertData.insert(Thread.currentThread());            };        }.start();        new Thread() {            public void run() {                insertData.insert(Thread.currentThread());            };        }.start();    }}class InsertData {    private ArrayList<Integer> arrayList = new ArrayList<Integer>();    public synchronized void insert(Thread thread){        for(int i=0;i<5000;i++){            System.out.println(thread.getName()+"在插入数据"+i);            arrayList.add(i);        }    }}

1)当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。

2)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,

3)如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型),也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题


synchronized代码块

synchronized代码块使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题,synchronized代码块可以实现只对需要同步的地方进行同步。

另外,每个类也会有一个锁,它可以用来控制对static数据成员的并发访问。并且如果一个线程执行一个对象的非static synchronized方法,另外一个线程需要执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。

public class Test {    public static void main(String[] args)  {        final InsertData insertData = new InsertData();        new Thread(){            @Override            public void run() {                insertData.insert();            }        }.start();        new Thread(){            @Override            public void run() {                insertData.insert1();            }        }.start();    }}class InsertData {    public synchronized void insert(){        System.out.println("执行insert");        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("执行insert完毕");    }    public synchronized static void insert1() {        System.out.println("执行insert1");        System.out.println("执行insert1完毕");    }}
public class Test {    public static void main(String[] args)  {        final InsertData insertData = new InsertData();        new Thread(){            @Override            public void run() {                insertData.insert();            }        }.start();        new Thread(){            @Override            public void run() {                insertData.insert1();            }        }.start();        new Thread(){            @Override            public void run() {                insertData.insert2();            }        }.start();        new Thread(){            @Override            public void run() {                insertData.insert3();            }        }.start();    }}class InsertData {    private Object object = new Object();    public synchronized void insert(){        System.out.println("执行insert");        try {            Thread.sleep(5000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("执行insert完毕");    }    public synchronized static void insert1() {        System.out.println("执行insert1");        System.out.println("执行insert1完毕");    }    public void insert2() {        synchronized (this) {            System.out.println("执行insert2");            System.out.println("执行insert2完毕");        }    }    public void insert3() {        synchronized (object) {            System.out.println("执行insert3");            System.out.println("执行insert3完毕");        }    }}
执行insert执行insert1执行insert1完毕执行insert3执行insert3完毕执行insert完毕执行insert2执行insert2完毕

  有一点要注意:对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。而sleep()不会释放占用的锁。

synchronized关键字

0 0