您的位置:首页 > 编程语言 > Java开发

秋招准备-Java-并发编程-同步机制与synchronized(二)

2018-04-12 05:43 387 查看


1.线程安全的一些概念
2.同步简述
3.synchronized的使用与说明

1.线程安全的一些概念

    1.正确性
        即某个类的行为与其规范完全一致,意思就是功能不能出错,使用多线程编程在带来性能提高的同时,也会由于多线程之间相互影响而导致的功能出错的情况,因此,线程安全,实际上就是确保,大到程序,小到类的方法的正确性,保证每个功能都按其设计的语义来实现,则可以说线程安全。

    
    2.原子性
        一个操作或多个操作,要么都执行,且执行过程不能被打断,要么就不执行,则为原子性。

        如对基本类型的访问和读写是原子性的。而“读取-修改-写入”(典型i++),“先检查后执行”(if语句)是非原子的

        原子操作是实现线程安全的一种,但一组原子操作的组合并不一定就是线程安全了,仍然需要一定的同步机制做确保。

    3.可见性

        指一个线程修改了共享变量的值,其他线程能够立即得知这个修改。

        主要通过原子类,volatile,synchronized来实现。

    4.有序性

        工作内存与主存同步延迟会导致可见性引起的线程不安全问题,而指令重排序则可能导致由有序性引起的线程不安全问题。
        指令重排序是一种JIT编译器的优化,目的是使处理器内部的运算单元能够尽量被充分利用,可能会将输入的代码进行乱序执行,(可以理解为a++;b++;会分到不同处理器一起执行,那么可能同时,也可能b++;在前。)

2.同步简述

    先从引起线程安全的问题讲起,大多数涉及线程安全的操作,都是基于这两点的。
    1.存在共享数据    2.存在多个线程访问使用共享数据

    因此在多个线程同一时段使用共享数据的时候,就有可能会因为相互干扰而导致出错。

    也许按我们自己写代码的顺序,按我们自己想要的各个线程之间执行的顺序,可以得到想要的结果。

    但是多线程在调用的时候,是受JVM来管理的,我们无法控制,因此,在基于几个线程的某种执行时序下,就有可能会导致错误的结果。

    而同步机制,其实就是通过它,来让线程按我们自己想要的执行顺序来执行,以确保线程安全。

    显然,保证线程安全并不全靠同步机制,从上述引起线程安全的两点来看,还有一些方法能够避开错误。如:
    1.如果不存在共享数据,同样可以使用多线程来实现多个只涉及自身的耗时操作的并发执行,以提高效率。

    2.使用自带的原子类,来实现共享数据的安全性。

    3.使用不可更改的共享数据,即共享数据属于不可变类。

    按我现在的理解,实际中,基本上就基于两点来考虑线程安全,1则是考虑数据线程私有,2则是考虑同步。并且最主要的也最清晰的方式,就是靠同步机制。

    而同步机制的主要实现,则是靠互斥实现-synchronized,与显示锁实现-Lock

    本章先介绍synchronized,下一站介绍Lock相关。

3.synchronized

    1.synchronized的使用方法
        主要即三种,synchronized代码块,synchronized方法,synchronized静态方法。

        其区别是加的锁对象不一样。

        synchronized有一个作用区域,且有一把锁,且锁是唯一的,只有持有锁,才能进入到作用区域,如果在申请锁时,发现锁已经被别的线程拿走了,就要进入阻塞且等待,直到那个拿锁的线程出作用区域的时候释放锁,然后阻塞状态的线程中的一个才能够获得锁进入作用区。

        synchronized的锁,是对象锁,即任何一个对象都有了一个新的作用,可以用来当锁,Object o = new Object(),那么对象o就可以用来当锁,且o是唯一的,Object o1 = new Object(),这样的o与o1,就是两把完全不同的锁,synchronized(o){……}这样就用o给这个代码块加上锁了,任何用o绑定的代码块,就都需要遵循synchronized的规则,进拿出放,申请拿锁,无则阻塞。
        理解完synchronized的对象锁,那么上述三种用法就是根据需求来用了。写一段实验代码:public class Main
{
private static Object o1 = new Object();
private Object o2 = new Object();
public static synchronized void staticFuncSyn() {
try {
Thread.sleep(2000);
}catch(Exception ex) {}
System.out.println("synchronized静态方法实现-对象锁的对象为:类锁Main.class");
}
public synchronized void funcSyn(){
try {
Thread.sleep(2000);
}catch(Exception ex) {}
System.out.println("synchronized方法实现-对象锁的对象:实例出来的对象");
}
public void func1() {
synchronized(o1) {
try {
Thread.sleep(2000);
}catch(Exception ex) {}
System.out.println("synchronized代码块实现-对象锁的对象:静态对象o1");
}
}
public void func2() {
synchronized(o2) {
try {
Thread.sleep(2000);
}catch(Exception ex) {}
System.out.println("synchronized代码块实现-对象锁的对象:成员对象o2");
}
}
public void func3() {
synchronized(this) {
try {
Thread.sleep(2000);
}catch(Exception ex) {}
System.out.println("synchronized代码块实现-对象锁的对象:实例出来的对象");
}
}
public static void main(String[] args)
{
Main A = new Main();
Thread th1 = new Thread(new Runnable() {
public void run() {
A.staticFuncSyn();
}
});
Thread th2 = new Thread(new Runnable() {
public void run() {
A.funcSyn();
}
});
Thread th3 = new Thread(new Runnable() {
public void run() {
A.func1();
}
});
Thread th4 = new Thread(new Runnable() {
public void run() {
A.func2();
}
});
Thread th5 = new Thread(new Runnable() {
public void run() {
A.func3();
}
});
th1.start();th2.start();th3.start();th4.start();th5.start();
/**
* 结果:th1-th4两秒后同时打印,再过了两秒后th5才打印
*/
}
}
    2.synchronized的特点

        <1>通过JVM来执行,使用对象锁对代码块进行加锁,结构清晰,出方法块自动解锁。

        <2>阻塞的等待队列无法跳出,一旦拿锁线程不释放锁,则阻塞线程会无限等待下去。

        <3>提供单一的条件队列,通过wait()/notify()/notifyAll()实现中断唤醒机制

        <4>在jdk6以前,当并发量高时,由于大量线程阻塞会导致性能极具下降,而jdk6优化以后,synchronized的性能越来越好。

    3.synchronized的优化(jvm层面)

        锁粗化、锁消除
        偏向锁、轻量级锁、自旋锁

        (未……)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息