likes
comments
collection
share

AndroidCoder 浅谈并发编程之线程共享

作者站长头像
站长
· 阅读数 23

前言

前面熟悉了并发编程的一些常见的基础概念,并且较为深入地认识了线程,可以说是并发编程浅入门啦。那本篇来学习一下线程间共享和相互协作知识块

概览

AndroidCoder 浅谈并发编程之线程共享

线程间共享和协作

在Java中,多个线程同时访问同一对象或者访问同一个对象里同一个成员变量,这就是线程间共享的一个很常见情况。熟悉过JVM内存模型的同学,肯定对共享资源比较清楚:

  • 堆:堆是在进程空间中开辟出来的,所以它是理所当然地被共享的,通过关键字new创建的是共享的
  • 全局变量:是与具体某一方法无关的,是与特定线程无关;因此也是共享的
  • 静态变量:是共享的
  • 文件等公用资源:是共享的

非共享的资源有:栈和寄存器

线程同步(Synchronizion)

在日常开发中,线程间共享成员变量是存在一些问题的,需要

class SharedThread {
    //成员变量 用于线程间共享
    private int count = 0;
    ...
    public void addCount(){
        count++; // 操作count变量
    }
    // 一个测试线程
    private static class CountThread extends Thread {
        private SharedThread sharedThread;
        public CountThread(SharedThread sharedThread) {
            this.sharedThread = sharedThread;
        }

        @Override
        public void run() {
            for (int i = 0; i < 6666; i++) {
                sharedThread.addCount();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        SharedThread sharedThread = new SharedThread();
        CountThread countThread = new CountThread(sharedThread);
        CountThread countThread1 = new CountThread(sharedThread);
        countThread.start();
        countThread1.start();
        Thread.sleep(66);
        System.out.println(sharedThread.getCount());
    }
}

以上示例运行结果:

7045

程序运行结束后,值是小于 13332,是由线程间共享的同步问题导致的,解决此问题需要加上 synchronized修饰。

把以上代码稍作修改,如:

class SharedThread {

    public synchronized void addCount() { // synchronized来修饰方法
        count++;
    }

}

修正后运行结果是没有问题的。

解决这个问题还有另一种方式

class SharedThread {
    private int count = 0;
    private Object object = new Object(); // 使用 Object 作为锁

    public void addCount(){
        synchronized (object) { // synchronized修饰对象
            count++;
        }
    }

}

以上两种方式没有什么差别,都是对象锁。

对象锁和类锁

// 对象锁
private synchronized void lockObject1() {  // 修饰成员函数
  SleepTool.second(2);
  System.out.println(“Object Lock1 use”);
  SleepTool.second(2);
  System.out.println(“Object Lock1 end”);
}

// 类锁,实际是锁类的class对象
private static synchronized void lockClass() { //修饰class对象
  SleepTool.second(2);
  System.out.println(“Class Lock use”);
  SleepTool.second(2);
  System.out.println(“Class Lock end”);
}

对象锁和对象锁之间是互不影响的,对象锁和类锁之间也是互不影响的。

线程等待(Wait)和通知(Notify)

等待、通知是属于线程间协作,一般由等待方获取锁之后进行条件检查,条件满足,则执行逻辑代码,否则不执行。

notifyAll() 方法会唤醒所有的线程,而 notify() 只会唤醒一个线程,且这个线程不一定是想唤醒的线程,因此在使用时最好使用 notifyAll() 方法。

对象锁,锁同一个对象,如果有多个线程执行方法,之间是不会有冲突,是因为调用 wait() 方法之后,会释放锁,供另外线程使用。

总结

在处理多线程共享情况时,那必须要想到对共享的方法或对象进行同步处理。可使用对象锁的方式synchronized关键字修饰即可。