synchronized

0.synchronized关键字

  • synchronized关键字锁定的是对象不是代码块
  • 锁定的对象有两种:1.类的实例 2.类对象(类锁)
  • 加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一。

1. 锁定的对象为类的实例举例

public class Demo {

    private int count = 10;
    private Object object = new Object();

    public void test(){
        synchronized (object){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }
   }
public class Demo {

    private int count = 10;

    public void test(){
        //synchronized(this)锁定的是当前类的实例,这里锁定的是Demo类的实例
        synchronized (this){
            count--;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    }

}
public class Demo {

    private int count = 10;

    //直接加在方法声明上,相当于是synchronized(this)
    public synchronized void test(){
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

}

2.锁定的是类对象

public class Demo {

    private static int count = 10;

    //synchronize关键字修饰静态方法锁定的是类的.class文件
    //静态方法中synchronize锁定代码块,锁定的对象不能是类的实例,只能是类的.class文件。
    public synchronized static void test(){
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

    public static void test2(){
        synchronized (Demo4.class){//这里不能替换成this
            count--;
        }
    }

}

3.锁对象的改变

  • 锁对象的改变
  • 锁定某对象o,如果o的属性发生改变,不影响锁的使用
  • 但是如果o变成另外一个对象,则锁定的对象发生改变
  • 应该避免将锁定对象的引用变成另外一个对象
public class Demo {

    Object o = new Object();

    public void test(){
        synchronized (o) {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            }
        }
    }

    public static void main(String[] args) {
        Demo1 demo = new Demo();

        new Thread(demo :: test, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread t2 = new Thread(demo :: test, "t2");

         demo.o = new Object();
        //t2能否执行?
        t2.start();//t1,t2交替输出
    }

}

4.不要以字符串常量作为锁定的对象

  • 在下面,test1和test2其实锁定的是同一个对象
public class Demo {

    String s1 = "hello";
    String s2 = "hello";

    public void test1(){
        synchronized (s1) {
            System.out.println("t1 start");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1 end");
        }
    }

    public void test2(){
        synchronized (s2) {
            System.out.println("t2 start");
        }
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(demo :: test1,"test1").start();
        new Thread(demo :: test2,"test2").start();
    }

}

5.锁的粒度

同步代码快中的语句越少越好

  • 比较test1和test2
  • 业务逻辑中只有count++这句需要sync,这时不应该给整个方法上锁
  • 采用细粒度的锁,可以使线程争用时间变短,从而提高效率
public class Demo{

    int count = 0;

    public synchronized void test1(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        count ++;

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void test2(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized (this) {
            count ++;
        }

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

6.线程重入

问题就在于线程重入的问题,

  • 第一个线程减了个1变成9了,还没打印,第二个线程又减了个1,第三个线程又减了个1,
  • 这时候虽然第一个线程只减了一个1但是却打印出来一个7(这里情况是不一定的)
  • 可以给方法加上synchronized
public class Demo1 implements Runnable{

    private int count = 10;

    @Override
  public/* synchronized*/ void run() {
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }


    public static void main(String[] args) {
        Demo1 demo = new Demo1();
        for (int i = 0; i < 5; i++) {
            new Thread(demo,"THREAD" + i).start();
        }
    }

}

public class Demo2 implements Runnable{

    private int count = 10;

    @Override
    public synchronized void run() {
        count--;
        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            //相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自的锁标记,可以同时执行。
            Demo2 demo = new Demo2();
            new Thread(demo,"THREAD" + i).start();
        }
    }

}

7.同步方法和非同步方法是否可以同时调用

同步方法和非同步方法是否可以同时调用?可以

public class Demo{

    public synchronized void test1(){
        System.out.println(Thread.currentThread().getName() + " test1 start...");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " test1 end...");
    }

    public void test2(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " test2");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(demo :: test1,"test1").start();
        new Thread(demo :: test2,"test2").start();
    }

}

8.脏读问题

脏读问题

  • 实际业务当中应该看是否允许脏读,不允许的情况下对读方法也要加锁
public class Demo {

    String name;
    double balance;

    public synchronized void set(String name,double balance){
        this.name = name;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.balance = balance;
    }

    public /*synchronized*/ double getBalance(String name){
        return this.balance;
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        new Thread(()->demo.set("dd",100.0)).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(demo.getBalance("dd"));//

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(demo.getBalance("dd"));
    }

}

9.一个同步方法调用另外一个同步方法,能否得到锁

一个同步方法调用另外一个同步方法,能否得到锁?可以,synchronized本身可支持重入

public class Demo {

    synchronized void test1(){
        System.out.println("test1 start.........");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        test2();
    }

    synchronized void test2(){
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test2 start.......");
    }

    public static void main(String[] args) {
        Demo demo= new Demo();
        demo.test1();
    }

}

10.重入锁的另外一种情况,继承

public class Demo {

    synchronized void test(){
        System.out.println("demo test start........");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("demo test end........");
    }

    public static void main(String[] args) {
            new Demo2().test();
    }

}

class Demo2 extends Demo {

    @Override
    synchronized void test(){
        System.out.println("demo2 test start........");
        super.test();
        System.out.println("demo2 test end........");
    }

}

11.T2线程能否执行?

public class Demo {

    int count = 0;

    synchronized void test(){
        System.out.println(Thread.currentThread().getName() + " start......");
        while (true) {
            count ++;
            System.out.println(Thread.currentThread().getName() + " count = " + count);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 5) {
                //碰到异常的情况,如果没有处理,会自动释放锁,所以T2可以执行。
                int i = 1/0;
            }
        }
    }

    public static void main(String[] args) {
        Demo demo11 = new Demo();

        Runnable r = () -> demo11.test();

        new Thread(r, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(r, "t2").start();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值