synchronized

一、为什么需要synchronized

看下面这段代码

/**
 * Created by yan on 2019/2/3.
 */
public class ThreadDemo implements Runnable {

    static ThreadDemo threadDemo = new ThreadDemo();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(threadDemo);
        Thread t2 = new Thread(threadDemo);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }

    static int i = 0;
    @Override
    public void run() {
        for (int j = 0; j < 10000; j++) {
            i++;
        }
    }
}

正常情况下应该输出10000*2 = 20000
我运行三次看下结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
很明显它是不等20000的
原因:当t1线程读取到count++后在写入内存之前t2线程读取count++,因此两个线程读取的是同一个数字(这种概率是相当大的),因此最终获得的数字远小于预期的值

二、synchronized的两种用法

对象锁

包括方法锁(默认对象为this当前实例对象)和同步代码块锁(自己指定锁对象)

类锁

synchronized修饰的静态方法,或指定锁的class对象

2.1、对象锁

代码块形式:手动指定锁对象
方法锁形式:synchronized修饰普通方法,锁对象默认为this

2.1.1、代码块形式锁
/**
 * Created by yan on 2019/2/3.
 */
public class SynchronizedObjectCodeBlok implements Runnable {

    static SynchronizedObjectCodeBlok instance = new SynchronizedObjectCodeBlok();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){  //当t1和t2两个线程都是存活状态时,做空循环

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        synchronized (this) {
            System.out.println("对象锁代码块形式" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "运行完毕");
        }
    }
}
线程1 线程2 run() 老二,咱俩一块走 老大,遵命 线程1:老二,我到了 大哥,你太胖了,我进不去 线程1 线程2 run()

有synchronized代码块:对象锁代码块形式Thread-0-----5s-----Thread-0运行完毕,对象锁代码块形式Thread-1-----5s-----Thread-1运行完毕,finished
无synchronized代码块:两个线程“同时"运行,“同时”结束

如果指定锁对象?
创建锁对象

Object lock = new Object();

用lock取代synchronized中的this
运行效果:和this相同

那什么时候使用指定锁对象呢?
当有不止一个synchronized代码块时,可以指定不同的锁对象

/**
 * Created by yan on 2019/2/3.
 */
public class SynchronizedObjectCodeBlok implements Runnable {

    static SynchronizedObjectCodeBlok instance = new SynchronizedObjectCodeBlok();

    //创建锁对象
    Object lock1 = new Object();
    Object lock2 = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){  //当t1和t2两个线程都是存活状态时,做空循环

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        synchronized (lock1) {
            System.out.println("对象锁代码块形式lock1--" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--lock1运行完毕");
        }
        synchronized (lock2) {
            System.out.println("对象锁代码块形式lock2--" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--lock2运行完毕");
        }
    }
}
线程1 线程2 lock1 lock2 老二,咱俩一块走 好的!看看咱俩谁快 老二,我先到了,我出去,你再进来 好的,大哥 老二,我进去了啊,你在哪呢? 大哥,我刚出发 线程1 线程2 lock1 lock2

效果:
第一个线程拿到锁lock1—>第一个线程lock1执行完毕(“同时”第一个线程拿到lock2,第二个线程拿到lock1)—> 第一个线程的lock2和第二个线程的lock1“同时”执行完毕(“同时第二个线程拿到lock2”)—>第二个线程lock2执行完毕---->finished

如果是3个线程,更或者是10个线程呢?(那还不如去算彩票的中奖规律呢,之后再说!!!!)

2.1.2、方法锁
/**
 * Created by yan on 2019/2/3.
 */
public class SynchronizedObjectMethod implements Runnable {

    static SynchronizedObjectMethod instance = new SynchronizedObjectMethod();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){  //当t1和t2两个线程都是存活状态时,做空循环

        }
        System.out.println("finished");
    }


    @Override
    public void run() {
        method();
    }
    
	//使用synchronized修饰方法,并在run方法中调用此方法
    public synchronized void method(){
        System.out.println("对象锁方法形式--"+Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--线程结束");
    }
}
线程1 线程2 synchronized方法 咱哥俩一块走 好的,大哥 线程1:老二,我先进去,你等会进 大哥,你不出去,我当然进不去了 线程1 线程2 synchronized方法

结果:第一个线程结束后第二个线程才开始运行

2.2、类锁

概念:java类中可能有很多个对象,但只有1个Class对象

类锁的使用方法:
1.synchronized加在static方法上
2.synchronized(*.class)代码块

2.2.1、synchronized加在static方法上

这里我们将类实例化了两次

/**
 * Created by yan on 2019/2/4.
 * 类锁---static形式
 */
public class SynchronizedClassStatic implements Runnable {

    static SynchronizedClassStatic instance1 = new SynchronizedClassStatic();
    static SynchronizedClassStatic instance2 = new SynchronizedClassStatic();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
        while(t1.isAlive() || t2.isAlive()){

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        methed();
    }
    
	//synchronized加在static方法上,在run方法中调用
    public static synchronized void methed(){
        System.out.println("static形式--"+Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--运行结束");
    }
}

有static的效果:第一个线程执行完后,第二个线程开始执行
五static的效果:两个线程“同时”开始,“同时”结束

2.2.2、synchronized(*.class)代码块
/**
 * Created by yan on 2019/2/4.
 * 类锁--- *.class
 */
public class SynchronizedClassClass implements Runnable {

    static SynchronizedClassClass instance1 = new SynchronizedClassClass();
    static SynchronizedClassClass instance2 = new SynchronizedClassClass();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
        while(t1.isAlive() || t2.isAlive()){

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        method();
    }

    public void method(){
        synchronized (SynchronizedClassClass.class){
            System.out.println("类锁*.class--"+ Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--线程结束");
        }
    }
}

有*.class效果:第一个线程结束后,第二个线程开始运行
换成 this :两个线程“同时”开始,“同时”结束

文章开篇的代码输出小于20000,我们学了sychronized的两大类–四种方法,感兴趣的可以试一下能不能得出20000

三、多线程访问同步方法的7种情况

1.两个线程同时访问一个对象的同步方法
2.两个线程同时访问两个对象的同步方法
3.两个线程同时访问synchronized的静态方法
4.两个线程同时访问同步方法和非同步方法
5.两个线程访问同一对象的不同的普通同步方法(非static)
6.两个线程同时访问静态synchronized非静态synchronized方法
7.方法抛出异常后,会释放锁(延伸扩展内容)

3.1、两个线程同时访问一个对象的同步方法

       2.1.2、方法锁

3.2、两个线程同时访问两个对象的同步方法

两个对象指两个实例 instance1,instance2创建两个线程

/**
 * Created by yan on 2019/2/3.
 */
public class SynchronizedObjectCodeBlok implements Runnable {

    static SynchronizedObjectCodeBlok instance1 = new SynchronizedObjectCodeBlok();
    static SynchronizedObjectCodeBlok instance2 = new SynchronizedObjectCodeBlok();



    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){  //当t1和t2两个线程都是存活状态时,做空循环

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        synchronized (this) {
            System.out.println("对象锁代码块形式--" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--运行完毕");
        }
        synchronized (this) {
            System.out.println("对象锁代码块形式--" + Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--运行完毕");
        }
    }
}
3.3、两个线程同时访问synchronized的静态方法

       2.2.2.1、synchronized加在static方法上

3.4、两个线程同时访问同步方法和非同步方法

我们设定一个线程访问synchronized的同步方法,另一个线程访问非同步(普通)方法,看代码:

/**
 * Created by yan on 2019/2/4.
 */
public class SynchronizedYesAndNoMethod implements Runnable {

    static SynchronizedYesAndNoMethod instance = new SynchronizedYesAndNoMethod();

    public static void main(String[] args) {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while(t1.isAlive() || t2.isAlive()){

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("Thread-0")){
            method1();
        }else{
            method2();
        }
    }

    //同步方法
    public synchronized void method1(){
        System.out.println("synchronized同步方法--"+Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--线程结束");
    }

    //非同步方法
    public void method2(){
        System.out.println("非同步(普通)方法--"+Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--线程结束");
    }
}

两个线程“同时”开始,“同时”结束,意思就是*非同步方法不受影响

3.5、两个线程访问同一对象的不同的普通同步方法(非static)
/**
 * Created by yan on 2019/2/4.
 */
public class SynchronizedDifferenceMethod implements Runnable {

    static SynchronizedDifferenceMethod instance = new SynchronizedDifferenceMethod();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){  //当t1和t2两个线程都是存活状态时,做空循环

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        method1();
        method2();
    }

    public synchronized void method1(){
        synchronized (SynchronizedClassClass.class){
            System.out.println("方法1--"+ Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--线程结束");
        }
    }

    public synchronized void method2(){
        synchronized (SynchronizedClassClass.class){
            System.out.println("方法2--"+ Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--线程结束");
        }
    }
}

方法1执行完两个线程,才执行方法2

3.6、两个线程同时访问静态synchronized和非静态synchronized方法(重要)
/**
 * Created by yan on 2019/2/4.
 */
public class SynchronizedStaticAndNormal implements Runnable {

    static SynchronizedStaticAndNormal instance = new SynchronizedStaticAndNormal();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        while (t1.isAlive() || t2.isAlive()){  //当t1和t2两个线程都是存活状态时,做空循环

        }
        System.out.println("finished");
    }

    @Override
    public void run() {
        if(Thread.currentThread().getName().equals("Thread-0")){
            method1();
        }else{
            method2();
        }
    }

    public static synchronized void method1(){
            System.out.println("static方法1--"+ Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--线程结束");
    }

    public synchronized void method2(){
            System.out.println("非static方法2--"+ Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"--线程结束");
        }
}

“同时”开始,“同时”结束

3.7、方法抛出异常后,会释放锁

不太容易表达,哎,睡觉!!!!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值