java多线程二(synchronized与volatile)

1. synchronized同步方法
线程不安全存在于“实例变量”中,如果是方法内部变量则是线程安全的。
如果多个线程访问1个对象的实例变量,则存在“非线程安全”。

synchronized产生的是对象锁,而不是把一段代码或者方法当锁,那个方法先执行synchronized方法就先获取对象锁,其他线程只能等待,前提是一个对象。
如果多个线程访问多个对象,则jvm产生多个锁,产生的效果可能是异步调用的效果。

public class Has {
    private int num=0;
    synchronized public void add(String name){
        try {
            if (name.equals("a")) {
                num=100;
                System.out.println("a set over");
                Thread.sleep(2000);
            }else if (name.equals("c")) {
                num=300;
                System.out.println("c set");
            } else {
                num=200;
                System.out.println("b set over");
            }
            System.out.println(name+"num"+num);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}
public class MyThread extends Thread{
    private Has has;
    public MyThread(Has has) {
        super();
        this.has=has;
    }
    @Override
    public void run(){
        super.run();
        has.add("a");
    }
}
public class MyThreadb extends Thread{
    private Has has;
    public MyThreadb(Has has) {
        super();
        this.has=has;
    }
    @Override
    public void run(){
        super.run();
        has.add("b");
    }
}
public class MyThreadc extends Thread{
    private Has has;
    public MyThreadc(Has has) {
        super();
        this.has=has;
    }
    @Override
    public void run(){
        super.run();
        has.add("c");
    }
}
public class Test {
    public static void main(String[] args) {
        Has hasa=new Has();
        Has hasb=new Has();
        Has hasc=new Has();
        MyThread aMyThread=new MyThread(hasa);
        aMyThread.start();
        MyThreadb bMyThread=new MyThreadb(hasb);
        bMyThread.start();
        MyThreadc cMyThread=new MyThreadc(hasc);
        cMyThread.start();
    }
}
结果
a set over
b set over
bnum200
c set
cnum300
anum100

共享只有共享的读写访问才需要同步,否则不需要同步。
**注意:同一个实例**A线程先持有object对象的Lock锁,B线程可以异步调用object对象中的非synchronized方法。
若A线程先持有object对象的Lock锁,B线程如果调用object对象中的synchronized方法则需要等待,同步。具体列子参考java多线程核心技术。
1.2.synchronized锁重入
锁重入:关键字synchronized具有锁重入的功能,在使用synchronized时,当一个线程得到对象的锁以后,再次请求该对象锁时是可以再次获取该对象的锁。
概率:自己可以再次获取自己的内部锁。如果不能锁重入,可能造成死锁。同时父子继承也具有锁重入。
1.3.同步不具有继承性,异常可以释放锁(可以终止线程)

2. synchronized同步代码块
synchronized同步方法存在弊端:线程A调用同步方法执行时间过长,那么B线程要等待较长时间。
使用:当两个并发线程访问 同一个对象的synchronized(this)同步代码块,一段时间只能被一个线程执行,另外一个线程要等执行完这个代码块以后才能执行该代码块。(类似同步方法,但是执行时间更短)
注意:在同一个方法中,同步代码块同步执行,其他方法异步执行。类似同步方法,(对象中一个上锁,一个不上锁,不上锁的可以异步调用。)
同时同步代码块也是锁对象,一个对象中两个方法,如果都带同步代码块,则先执行完一个在执行另外一个

3.将任意对象作为对象监视器
这里写图片描述synchronized(非this对象x)同步代码块。
锁非this对象的优点:如果一个类中有多个synchronized方法,这时候虽然能同步但是受到阻塞,影响效率,但如果是synchronized(非this)代码块中的程序与同步方法是异步的。
这里写图片描述
3. synchronized类锁
synchronized加到class或者静态方法上,变成了类锁。也能实现同步方法。
如果一个对象中出现了类锁和对象锁,可能出现异步的情况。原因是类锁和对象锁不是同一类锁。
这里写图片描述这里写图片描述
重点类锁,不同的实例对象去访问类锁,会形成同步的效果。
4. String常量池
String a=”1”;
String b=”1”;
a==b返回结果是true
因此synchronized(非this),不能直接放string常量,要new 一个常量对象。
4. 死锁
多线程会造成死锁,进入jdk的bin目录下,执行jps查询执行线程,然后jstack命令查询详细信息。

5内置类
1.内置类有两个同步方法,使用的是不同的锁,则结果会出现异步现象。
2.同步代码块synchronized(class2)对class2上锁后,其他线程只能以同步的方式调用class2中的静态同步方法。

对象的内置锁和对象的状态之间是没有内在的关联的,虽然大多数类都将内置锁用做一种有效的加锁机制,但对象的域并不一定通过内置锁来保护。当获取到与对象关联的内置锁时,并不能阻止其他线程访问该对象,当某个线程获得对象的锁之后,只能阻止其他线程获得同一个锁。之所以每个对象都有一个内置锁,是为了免去显式地创建锁对象。

6.volatile关键字
主要作用:使变量在多线程间是可见的。
使用volatile关键字,强制从公共内存中读取变量的值,内存结果如下。这里写图片描述

7.synchronized和volatile的区别
1)关键字 volatile是线程同步轻量级实现,性能比synchronized好,但是volatitle只能修饰变量,synchronized可以修饰方法,代码块。
2)多线程访问synchronized会阻塞,而volatile不会。
3)volatile能保证数据可见性,但是不能保证原子性。synchronized能够保证原子性,同时保证线程可见性,因为它是将私有内存和共有内存中的数据进行通报。
4)关键字volatile是解决变量在多线程间的可见性,而synchronized是解决多个线程间访问资源的同步性。

8.volatitle出现非线程安全的原因
1)read和load阶段:从主存复制变量到当前线程工作内存。
2)use和assign阶段:执行代码,改变共享变量值。
3)store和write阶段:用工作内存数据刷新主存对应变量的值。
变量在内存中工作过程

i++不是原子操作,使用synchronized可以进行原子操作,同时ActomicInterger原子类也可以进行原子操作。

9.Synchronized 原理
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1 ,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
执行monitorexit的线程必须是objectref所对应的monitor的所有者。
指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
  通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值