Synchornized使用及原理

Synchornized 方法

当一个线程试图访问同步代码块或对象的方法时,它首先必须得到锁,退出或抛出异常时必须释放锁。Java中的每一个对象都可以作为锁,具体表现为:
1. 对于普通同步方法,锁是当前实例对象
2. 对于同步方法块,锁是Synchonized括号里配置的对象
3. 对于静态同步方法,锁是当前类的Class对象

对于普通同步方法,锁是当前实例对象

不同线程对于同一个对象的锁互斥同步

  • 锁是当前实例对象,A线程获得了object对象的锁,B线程想在访问该同步方法必须等A线程将object对象锁释放。
package objective2.action1;

public class IncreaseHander {

    public static void main(String[] args) {
        IncrementRunner incrementRunner1 = new IncrementRunner("a");
        new Thread(incrementRunner1).start();
        new Thread(incrementRunner1).start();
    }
}

class IncrementRunner implements Runnable {
    private int num;
    private String name;

    public IncrementRunner(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        add(name);
    }

    public synchronized void add(String name) {
        if (name.equals("a")) {
            num = 100;
            System.out.println(Thread.currentThread().getName() + " a set over ");

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        } else {
            num = 200;
            System.out.println(Thread.currentThread().getName() + " other set over");
        }
        System.out.println(Thread.currentThread().getName() + " " + name + " num=" + num);
    }

}
Thread-0 a set over 
Thread-0 a num=100
Thread-1 a set over 
Thread-1 a num=100

可以看出,上面程序按线程0和线程1是按同步方式运行的。首先线程0获得对象incrementRunner1 的锁,在完成设置值和休眠后释放该对象锁,由线程1获得,继续做相同的操作。

不同线程对于不同对象的锁可以异步

  • synchornized获得的是对象锁,若是多个对象,JVM会创建多个锁
package objective2.action1;

public class IncreaseHander {

    public static void main(String[] args) {
        IncrementRunner r1 = new IncrementRunner("a");
        IncrementRunner r2 = new IncrementRunner("a");
        new Thread(r1).start();
        new Thread(r2).start();
    }
}

class IncrementRunner implements Runnable {
    private int num;
    private String name;

    public IncrementRunner(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        add(name);
    }

    public synchronized void add(String name) {
        if (name.equals("a")) {
            num = 100;
            System.out.println(Thread.currentThread().getName() + " a set over ");

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        } else {
            num = 200;
            System.out.println(Thread.currentThread().getName() + " other set over");
        }
        System.out.println(Thread.currentThread().getName() + " " + name + " num=" + num);
    }

}
Thread-1 a set over 
Thread-0 a set over 
Thread-0 a num=100
Thread-1 a num=100

上面例子,两个线程分别访问同一个类的两个不同的实例的相同名称的同步方法,效果却是以异步的方式运行的。 由于创建了两个对象,在系统中产生了两个锁,线程1不需要在线程0将锁释放后才能在进入add方法。

对象锁对于非synchrnoized方法异步

  • A线程先持有object对象的锁, B线程可以以异步方式调用object对象中的非synchrnoized类型方法
package objective2.action1;

public class Run {

    public static void main(String[] args) {
        MyObject object = new MyObject();
        ObjectRunner A = new ObjectRunner("A", object);
        ObjectRunner B = new ObjectRunner("B", object);
        new Thread(A).start();
        new Thread(B).start();
    }

}

class ObjectRunner implements Runnable {
    private String methodName;
    private MyObject object;

    public ObjectRunner(String methodName, MyObject object) {
        this.methodName = methodName;
        this.object = object;
    }

    @Override
    public void run() {
        if (methodName.equals("A")) {
            System.out.println("start call method A");
            object.methodA();
        } else {
            System.out.println("start call method B");
            object.methodB();
        }

    }

}

class MyObject {
    public synchronized void methodA() {
        try {
            String threadName = Thread.currentThread().getName();
            System.out.println("begin method A ThreadName" + threadName);
            Thread.sleep(2000);
            System.out.println("end method A ThreadName" + threadName);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void methodB() {
        try {
            String threadName = Thread.currentThread().getName();
            System.out.println("begin method B ThreadName=" + threadName);
            Thread.sleep(2000);
            System.out.println("end method B ThreadName=" + threadName);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
start call method A
start call method B
begin method B ThreadName=Thread-1
begin method A ThreadNameThread-0
end method B ThreadName=Thread-1
end method A ThreadNameThread-0

将上面程序做一个简单改动,运行结果完全不一样:程序运行的顺序是异步的。

对象锁对于synchrnoized方法同步

  • A线程先持有object对象的锁,B线程如果这时调用object对象中的synchrnoized类型方法需要等待
package objective2.action1;

public class Run {

    public static void main(String[] args) {
        MyObject object = new MyObject();
        ObjectRunner A = new ObjectRunner("A", object);
        ObjectRunner B = new ObjectRunner("B", object);
        new Thread(A).start();
        new Thread(B).start();
    }

}

class ObjectRunner implements Runnable {
    private String methodName;
    private MyObject object;

    public ObjectRunner(String methodName, MyObject object) {
        this.methodName = methodName;
        this.object = object;
    }

    @Override
    public void run() {
        if (methodName.equals("A")) {
            System.out.println("start call method A");
            object.methodA();
        } else {
            System.out.println("start call method B");
            object.methodB();
        }

    }

}

class MyObject {
    public synchronized void methodA() {
        try {
            String threadName = Thread.currentThread().getName();
            System.out.println("begin method A ThreadName" + threadName);
            Thread.sleep(2000);
            System.out.println("end method A ThreadName" + threadName);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void methodB() {
        try {
            String threadName = Thread.currentThread().getName();
            System.out.println("begin method B ThreadName=" + threadName);
            Thread.sleep(2000);
            System.out.println("end method B ThreadName=" + threadName);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

start call method A
start call method B
begin method A ThreadNameThread-0
end method A ThreadNameThread-0
begin method B ThreadName=Thread-1
end method B ThreadName=Thread-1

线程0先持有object对象的锁,线程1如果这时调用object对象中的synchrnoized类型方法需要等待,即使调用的是不同的方法。
也就是说,对象的锁能锁住对象所有的synchrnoized方法。
总之还是那句话,对于普通同步方法,锁是当前实例对象。方法A,方法B的锁都是object对象。一个线程访问该方法(拿到锁),其它线程必须等待。

synchrnoized锁可重入

  • synchrnoized锁可重入
    当某个线程请求一个由其它线程持有的锁时,发出请求的线程就会堵塞。然而,由于内置锁是可入的,因此如果某个线程试图获得一个已经由自己或自己父类持有的锁,那么这个请求就会成功。
package objective2.action1;

public class RunService {

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }

}

class MyThread extends Thread {

    @Override
    public void run() {
        Service service = new Service();
        service.service1();
    }
}

class Service {
    public synchronized void service1() {
        System.out.println(Thread.currentThread().getName() + " call Service 1");
        service2();
    }

    public synchronized void service2() {
        System.out.println(Thread.currentThread().getName() + " call Service 2");
    }
}
Thread-0 call Service 1
Thread-0 call Service 2

“可重入锁”的概念是:自己可以再次获取自己的内部锁。 如上面程序运行,在调用方法service1时,线程0获取service对象的锁,此时锁还没有释放,调用方法service2时,又一次获取到了service对象的锁。如果同一线程不可重入锁,就会造成死锁。

  • 可重入锁支持在父子继承环境中
package objective2.action1;

public class RunService {

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }

}

class MyThread extends Thread {

    @Override
    public void run() {
        Service service = new SubService();
        service.service2();
    }
}

class Service {
    public synchronized void service1() {
        System.out.println(Thread.currentThread().getName() + " call Service 1");
    }

    public synchronized void service2() {
        System.out.println(Thread.currentThread().getName() + " call Service 2");
    }
}

class SubService extends Service {
    public synchronized void service2() {
        System.out.println(Thread.currentThread().getName() + " call Sub Service 2");
        super.service1();
    }
}
Thread-0 call Sub Service 2
Thread-0 call Service 1

同步不能继承

  • 同步不具有继承性
package objective2.action1;

public class TestService {
    public static void main(String[] args) {
        Sub sub = new Sub();
        ServiceRunner serviceRunner = new ServiceRunner(sub);
        new Thread(serviceRunner, "A").start();
        new Thread(serviceRunner, "B").start();

    }
}

class ServiceRunner implements Runnable {
    private Sub sub;

    public ServiceRunner(Sub sub) {
        this.sub = sub;
    }

    @Override
    public void run() {
        sub.service();
    }

}

class Main {

    public synchronized void service() {
        String threadName = Thread.currentThread().getName();
        System.out.println("in main, ThreadName=" + threadName + " beginTime=" + System.currentTimeMillis());
        sleep(1000);
        System.out.println("in main, ThreadName=" + threadName + " endTime=" + System.currentTimeMillis());

    }

    protected void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

class Sub extends Main {
    public void service() {
        String threadName = Thread.currentThread().getName();
        System.out.println("in Sub, ThreadName=" + threadName + " beginTime=" + System.currentTimeMillis());
        sleep(1000);
        System.out.println("in Sub, ThreadName=" + threadName + " endTime=" + System.currentTimeMillis());
        super.service();
    }
}
in Sub, ThreadName=B beginTime=1496618308264
in Sub, ThreadName=A beginTime=1496618308264
in Sub, ThreadName=B endTime=1496618309266
in Sub, ThreadName=A endTime=1496618309266
in main, ThreadName=B beginTime=1496618309266
in main, ThreadName=B endTime=1496618310267
in main, ThreadName=A beginTime=1496618310267
in main, ThreadName=A endTime=1496618311268

父类Main的方法加synchronized,子类Sub继承该方法但是不加synchronized,从运行结果看:A,B两个线程几乎同时进入子类非同步方法,是异步执行的。但是在主类中由于加了锁,必须B释放锁A才能进入。

  • 子类与父类是同一个锁
package objective2.action1;

public class TestService {
    public static void main(String[] args) {
        Sub sub = new Sub();
        ServiceRunner serviceRunner = new ServiceRunner(sub);
        new Thread(serviceRunner, "A").start();
        new Thread(serviceRunner, "B").start();

    }
}

class ServiceRunner implements Runnable {
    private Sub sub;

    public ServiceRunner(Sub sub) {
        this.sub = sub;
    }

    @Override
    public void run() {
        sub.service();
    }

}

class Main {

    public synchronized void service() {
        String threadName = Thread.currentThread().getName();
        System.out.println("in main, ThreadName=" + threadName + " beginTime=" + System.currentTimeMillis());
        sleep(1000);
        System.out.println("in main, ThreadName=" + threadName + " endTime=" + System.currentTimeMillis());

    }

    protected void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

class Sub extends Main {
    public synchronized void service() {
        String threadName = Thread.currentThread().getName();
        System.out.println("in Sub, ThreadName=" + threadName + " beginTime=" + System.currentTimeMillis());
        sleep(1000);
        System.out.println("in Sub, ThreadName=" + threadName + " endTime=" + System.currentTimeMillis());
        super.service();
    }
}
in Sub, ThreadName=A beginTime=1496618631925
in Sub, ThreadName=A endTime=1496618632926
in main, ThreadName=A beginTime=1496618632926
in main, ThreadName=A endTime=1496618633926
in Sub, ThreadName=B beginTime=1496618633926
in Sub, ThreadName=B endTime=1496618634927
in main, ThreadName=B beginTime=1496618634927
in main, ThreadName=B endTime=1496618635927

在子类中也加上锁,A,B两个线程运行同步,只有A释放了锁,B才能拿到锁进入方法。
不管子类还是父类,在这个例子中,锁是sub对象,一个线程拿到了该对象的锁,其它线程必须等待该线程释放。

这么多例子,其实就是一句话, 对于普通同步方法,锁是当前实例对象。

对于同步方法块,锁是Synchonized括号里配置的对象

synchornized 方法的弊端

对于普通同步方法,锁是当前实例对象,一个线程拿到了该对象的锁,其它线程必须等待该线程释放。虽然解决了线程安全的问题,但是,很大程度上也牺牲了响应速度,性能。
还是用【买票例子】:5个售票员出售10张票,不能不同售票员出售同一张票,每买一张耗时1s,直到票出售完为止。
如下程序使用synchornized 方法实现该功能,虽然解决了线程安全的问题,但弊端很明显,任何一个线程拿到该对象的锁,其他线程都得等它释放锁才能进入该方法。10张票卖完要10s,性能很差。

package objective2.action1;

import java.util.concurrent.CountDownLatch;

public class Counter {

    private static int tickets = 10;
    private static int threadNum = 5;
    private static CountDownLatch timeLatch = new CountDownLatch(threadNum);

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        Runnable counter = new CounterRunner(tickets, timeLatch);
        Thread[] threads = new Thread[threadNum];

        for (int i = 0; i < threadNum; i++) {
            threads[i] = new Thread(counter, "售票员" + i);

        }
        for (Thread thread : threads) {
            thread.start();
        }

        try {
            timeLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long takeTime = System.currentTimeMillis() - startTime;
        System.out.println("takeTime=" + takeTime + "ms");
    }

}

class CounterRunner implements Runnable {
    private int tickets;
    private CountDownLatch timeLatch;

    public CounterRunner(int tickets, CountDownLatch timeLatch) {
        this.tickets = tickets;
        this.timeLatch = timeLatch;
    }

    @Override
    public void run() {
        while (tickets > -1) {
            if (saleTickets()) {
                break;
            }
        }
    }

    /**
     * 
     * 买票实现
     * 
     * @return 卖完返回true,没卖完返回false
     */
    private synchronized boolean saleTickets() {
        String threadName = Thread.currentThread().getName();
        if (tickets == 0) {
            System.out.println(threadName + " 没票了");
            timeLatch.countDown();
            return true;
        } else {
            System.out.println(threadName + " 出售票号" + tickets--);
            sleep(1000);
        }
        return false;
    }

    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
售票员0 出售票号10
售票员0 出售票号9
售票员0 出售票号8
售票员3 出售票号7
售票员4 出售票号6
售票员4 出售票号5
售票员2 出售票号4
售票员1 出售票号3
售票员2 出售票号2
售票员2 出售票号1
售票员4 没票了
售票员3 没票了
售票员0 没票了
售票员2 没票了
售票员1 没票了
takeTime=10009ms

使用同步代码块解决弊端

package objective2.action1;

import java.util.concurrent.CountDownLatch;

public class Counter {

    private static int tickets = 10;
    private static int threadNum = 5;
    private static CountDownLatch timeLatch = new CountDownLatch(threadNum);

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        Runnable counter = new CounterRunner(tickets, timeLatch);
        Thread[] threads = new Thread[threadNum];

        for (int i = 0; i < threadNum; i++) {
            threads[i] = new Thread(counter, "售票员" + i);

        }
        for (Thread thread : threads) {
            thread.start();
        }

        try {
            timeLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long takeTime = System.currentTimeMillis() - startTime;
        System.out.println("takeTime=" + takeTime + "ms");
    }

}

class CounterRunner implements Runnable {
    private int tickets;
    private CountDownLatch timeLatch;

    public CounterRunner(int tickets, CountDownLatch timeLatch) {
        this.tickets = tickets;
        this.timeLatch = timeLatch;
    }

    @Override
    public void run() {
        while (tickets > -1) {
            if (saleTickets()) {
                break;
            }
        }
    }

    /**
     * 
     * 买票实现
     * 
     * @return 卖完返回true,没卖完返回false
     */
    private boolean saleTickets() {
        String threadName = Thread.currentThread().getName();
        if (tickets == 0) {
            System.out.println(threadName + " 没票了");
            timeLatch.countDown();
            return true;
        } else {
            synchronized (this) {
                System.out.println(threadName + " 出售票号" + tickets--);
            }
            sleep(1000);
        }
        return false;
    }

    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
售票员0 出售票号10
售票员3 出售票号9
售票员4 出售票号8
售票员1 出售票号7
售票员2 出售票号6
售票员1 出售票号5
售票员0 出售票号4
售票员3 出售票号3
售票员4 出售票号2
售票员2 出售票号1
售票员3 没票了
售票员0 没票了
售票员1 没票了
售票员4 没票了
售票员2 没票了
takeTime=2004ms

从运行结果看,使用Synchornized代码块,在线程安全情况下,运行时间缩短,性能改善。

将任意对象作为对象监视器

synchronized(非this对象x) 格式的写法是将x对象本身作为“对象监视1器”(和synchronized(this)类似,只不过将当前类
对象改成x对象),也有如下结论:
1、当多个线程同时执行synchroized(x){}同步代码块时呈现同步效果。
2、当其他线程执行x对象中synchronized同步方法呈现同步效果
3、当其他线程执行x对象里面的synchroized(this)代码块或方法,也呈现同步效果。
总之一句话,对于同步方法块,锁是Synchonized括号里配置的对象,对象相同就同步,不同就异步。
eg.创建一个只能加入一个元素的List.

package objective2.action1;

import java.util.ArrayList;
import java.util.List;

public class RunOneList {

    public static void main(String[] args) {
        MyOneList myOneList = new MyOneList();
        MyService myService1 = new MyService(myOneList);
        new Thread(myService1, "A").start();
        new Thread(myService1, "B").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("myOneList Size = " + myOneList.getSize());
    }

}

class MyService implements Runnable {
    private MyOneList myOneList;

    public MyService(MyOneList myOneList) {
        this.myOneList = myOneList;
    }

    @Override
    public void run() {
        addService();

    }

    private void addService() {
        if (myOneList.getSize() < 1) {
            String data = getDataFromRemote();
            myOneList.add(data);
        }
    }

    private String getDataFromRemote() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "data";
    }
}

class MyOneList {
    private List list = new ArrayList();

    public synchronized void add(String data) {
        list.add(data);
    }

    public synchronized int getSize() {
        return list.size();
    }
}
myOneList Size = 2

从运行结果看,虽然MyOneList的方法都使用synchronized保证同步,但是add和getSize非原子操作,两线程以异步方式返回了MyOneList Size大小,导致脏读。

package objective2.action1;

import java.util.ArrayList;
import java.util.List;

public class RunOneList {

    public static void main(String[] args) {
        MyOneList myOneList = new MyOneList();
        MyService myService1 = new MyService(myOneList);
        new Thread(myService1, "A").start();
        new Thread(myService1, "B").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("myOneList Size = " + myOneList.getSize());
    }

}

class MyService implements Runnable {
    private MyOneList myOneList;

    public MyService(MyOneList myOneList) {
        this.myOneList = myOneList;
    }

    @Override
    public void run() {
        addService();

    }

    private void addService() {
        String data = getDataFromRemote();
        synchronized (myOneList) {
            if (myOneList.getSize() < 1) {
                myOneList.add(data);
            }
        }

    }

    private String getDataFromRemote() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "data";
    }
}

class MyOneList {
    private List list = new ArrayList();

    public synchronized void add(String data) {
        list.add(data);
    }

    public synchronized int getSize() {
        return list.size();
    }
}
myOneList Size = 1

Synchornized 的实现原理

你可能感兴趣的:(java,多线程并发)