黑马程序员技术交流社区

标题: 【广州校区】【原创】Java多线程技术 [打印本页]

作者: 吉山小学生    时间: 2017-12-20 15:20
标题: 【广州校区】【原创】Java多线程技术
声明和开启一个线程
当我们创建一个线程时,必须告诉这个线程将要运行什么代码!有两种方式来实现它:
同步
多个线程的之间的通信主要是通过访问共享数据,例如字段和字段引用的对象,这种通信的方式是极其方便的,但是也可能会产生两中错误—–线程干扰和内存一致性错误。阻止这些错误的工具就是同步(synchronization)。
同步方法
​ Java语言提供两种基本的同步语法:同步方法和同步语句。
​ 要使方法同步,只需将同步关键字synchronization添加到方法的声明中即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }
   
    public synchronized void decrement() {
        c--;
    }
   
    public synchronized int value() {
        return c;
    }
}
如果counter_obj是SynchronizedCounter的一个实例。那么对同步方法的调用会有如下影响。
需要注意的是构造方法不能声明为同步的,在构造方法的声明上使用synchronization关键字是语法错误。
同步语句
上一节我们在讨论同步方法的时候,出现了一个概念内部锁(intrinsic lock)。在讨论同步语句之前,我们来学习一下内部锁和同步的关系。
其实同步是建立在一个名为内部锁(intrinsic lock ),也叫监视器锁(minitor lock),在API文档中也称之为监视器(minitor)。每一个实例都一个与之关联的内部锁。 当一个线程想要单独的访问某实例的字段时,线程不得不先要获得这个实例的内部锁,线程完成操作后就会释放内部锁。在线程获得内部锁和线程释放内部锁这段期间内,我们称之为线程持有内部锁。只要一线程持有某实例的内部锁,试图获得这个内部锁的其他线程那么就会阻塞。也就是同一时刻只能有一个线程持有同一内部锁。
当同步方法在线程执行时,线程就会自动的获得同步方法调用者的内部锁。同步方法返回后(也就是方法执行完毕)线程释放内部锁。你可能会有疑惑一个静态(static)的同步方法被调用将会发生什么,因为静态方法的调用者是类,而不是一个实例。因为每个类其实就是Class类的一个实例,因此每个类中也有与之相关的内部锁,当调用静态同步方法的时候,线程会自动获得当前类的内部锁。
下面我们看看同步语句的语法:
1
2
3
4
5
6
7
public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}
在上面的例子中我们能看到,首先,与同步方法不同,同步语句必须指定提供内部锁的实例。其次,addName 方法同步 改变lastName和nameCount,又避免了同步调用其他实例方法,这里的“其他实例方法的调用”指的是nameList.add(name),因为在同步代码中调用其他实例的方法可能产生被称为liveness的问题(以后我们会讨论这个问题)。所以同步在并发时能够实现更细粒度的同步。
下面我们再看一个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}
类MsLunch有两个个字段C1和C2,它们永远不会同时使用。这些字段的所有更新必须同步,但允许他们更新时能够交错执行。我们就可以像例子那样,使用两个对象提供锁,以代替同步方法和使用this的同步语句。这样减少了不必要的阻塞增加了并发性。
可重入同步
通过前面的学习我们知道,一个线程不能获得以被其他线程持有的内部锁,但是一个线程能够再次获得自已已持有的锁。也就是说同一线程可以多次获得同一把内部锁,这保证可重入同步。留一个思考,想想下面的描述的场景如果不支持可重入同步将会发生什么现象?——–当同步代码中直接或者间接的调用了一个方法,而这个方法也有同步代码,并且这两个同步代码使用的是同一把锁。


作者: 杀猪的蛮汉    时间: 2017-12-21 17:24
感谢黑马老师分享
作者: java愤怒小鸟    时间: 2017-12-22 09:41
感谢分享
作者: 帅气de路人甲    时间: 2017-12-29 09:44
已学习,谢谢
作者: 一Pi小小小黑马    时间: 2018-1-11 21:28





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2