Java多线程编程笔记

  • synchronized
  • synchronized同步方法
  • synchronized同步代码块
  • 小结
  • 生产者 / 消费者模式实现
  • 一生产一消费:操作值
  • 多生产多消费:操作值
  • 多生产多消费:操作栈
  • Lock的使用
  • ReentrantLock类的使用
  • ReentrantReadWriteLock类的使用
  • 总结


Java多线程编程核心技术
链接:https://pan.baidu.com/s/1waW7B9PTmkNMS-qkh6LNKg 提取码:cm4i

synchronized

synchronized同步方法

  1. 方法内的变量为线程安全的(因为局部变量在栈中,又因为栈是线程私有的,线程安全)
  2. 实例变量非线程安全(实例对象存在堆中,堆是线程共享区域,非线程安全)
  3. 多个对象会有多个锁
  4. synchronized锁可重入性(持有锁A后,可调用其它需要持有锁A的方法,若不可重入则会死锁)
  5. 出现异常时,锁会自动释放
  6. 同步不能被子类继承

synchronized同步代码块

  1. synchronized方法的弊端(当锁住的方法执行时间特别长时,等待该锁的其他线程就需要等待较长时间,推荐使用同步代码块锁关键代码,也就是需要同步的某一块代码)
  2. 使用String对象做监视器时,应着重注意(String对象存在于常量池或堆中)
  3. 锁对象的改变(对于锁对象而言,只要这个对象没变,即使修改了它的属性,结果还是同步的。例如对于一个student对象而言,当其作为锁对象存在时,修改它的学号姓名都没有问题,结果依旧会是同步的)
  4. synchronized(非 this 对象 x)可得出三个结论:
  • 多个线程执行synchronized(x){}代码块时是同步效果
  • 其它线程执行对象 x 中的synchronized方法时是同步效果
  • 其它线程执行对象 x 中的synchronized(this){}代码块时是同步效果

小结

  • synchronized(class){} 的锁是类
  • synchronized(this){} 的锁是本类的实例对象
  • synchronized(非 this 对象 x){} 的锁是实例对象 x
  • synchronized public void set() {} 的锁是本类的实例对象
  • synchronized public static void set() {} 的锁是类

注意:多线程情况下,只有持有相同的锁,才能达到同步效果

生产者 / 消费者模式实现

原理都是基于wait和notify来实现的

一生产一消费:操作值

public class ValueObject {
    public static String value = "";
}

生产者

public class P {
    private String lock;
    public P(String lock){
        this.lock = lock;
    }

    public void setValue(){
        try {
            synchronized (lock){
                if (!ValueObject.value.equals("")){
                    lock.wait();
                }

                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                ValueObject.value = value;
                System.out.println("set的值是:" + value);
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

消费者

public class C {
    private String lock;
    public C(String lock){
        this.lock = lock;
    }

    public void getValue(){
        try {
            synchronized (lock){
                if (ValueObject.value.equals("")){
                    lock.wait();
                }
                String value = ValueObject.value;
                System.out.println("get的值是:" + value);
                ValueObject.value = "";
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    // 单个生产者消费者
    public static void main(String[] args) {
        String lock = "";
        P p = new P(lock);
        C c = new C(lock);

        ThreadP threadP = new ThreadP(p);
        ThreadC threadC = new ThreadC(c);

        threadP.start();
        threadC.start();
    }

}

class ThreadC extends Thread{
    private C c;

    public ThreadC(C c){
        this.c = c;
    }

    @Override
    public void run() {
        while (true){
            c.getValue();
        }
    }

}

class ThreadP extends Thread{
    private P p;

    public ThreadP(P p){
        this.p = p;
    }

    @Override
    public void run() {
        while (true){
            p.setValue();
        }
    }
}

多生产多消费:操作值

生产者

public class P {
    private String lock;
    public P(String lock){
        this.lock = lock;
    }

    public void setValue(){
        try {
            synchronized (lock){
                while (!ValueObject.value.equals("")){
                    lock.wait();
                }

                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                ValueObject.value = value;
                System.out.println("生产者:"+ Thread.currentThread().getName() + value);
                lock.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

消费者

public class C {
    private String lock;
    public C(String lock){
        this.lock = lock;
    }

    public void getValue(){
        try {
            synchronized (lock){
                while (ValueObject.value.equals("")){
                    lock.wait();
                }
                String value = ValueObject.value;
                System.out.println("消费者:"+ Thread.currentThread().getName() + value);
                ValueObject.value = "";
                lock.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        String lock = "";
        P p = new P(lock);
        C c = new C(lock);

        ThreadP[] threadP = new ThreadP[2];
        ThreadC[] threadC = new ThreadC[2];

        for (int i = 0; i < 2; i++) {
            threadP[i] = new ThreadP(p);
            threadP[i].setName("生产者" + (i+1));
            threadC[i] = new ThreadC(c);
            threadC[i].setName("消费者" + (i+1));
            threadP[i].start();
            threadC[i].start();
        }
    }
}

class ThreadC extends Thread{
    private C c;

    public ThreadC(C c){
        this.c = c;
    }

    @Override
    public void run() {
        while (true){
            c.getValue();
        }
    }

}

class ThreadP extends Thread{
    private P p;

    public ThreadP(P p){
        this.p = p;
    }

    @Override
    public void run() {
        while (true){
            p.setValue();
        }
    }
}

上述代码若使用notify唤醒单个线程,可能会造成“ 假死 ”(notify唤醒同类线程造成假死),所以使用notifyAll唤醒所有线程

多生产多消费:操作栈

public class MyStack {
    private List list = new ArrayList();

    synchronized public void push() {
        try {
            while (list.size() == 1) {
                this.wait();
            }
            list.add("anything: " + Math.random());
            this.notifyAll();
            System.out.println("push=" + list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public void pop() {
        try {
            while (list.size() == 0) {
                this.wait();
            }
            System.out.println(list.get(0));
            list.remove(0);
            this.notifyAll();
            System.out.println("pop=" + list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

生产者

public class Provider {
    private MyStack stack;

    public Provider(MyStack stack){
        this.stack = stack;
    }

    public void pushService(){
        stack.push();
    }
}

消费者

public class Consumer {
    private MyStack stack;

    public Consumer(MyStack stack){

        this.stack = stack;
    }

    public void popService(){
        stack.pop();
    }
}
public class Run {
    public static void main(String[] args) {
        MyStack stack = new MyStack();
        Provider provider = new Provider(stack);
        Consumer consumer = new Consumer(stack);
        Provider provider1 = new Provider(stack);

        CThread cThread = new CThread(consumer);
        PThread pThread = new PThread(provider);
        PThread pThread1 = new PThread(provider1);
        cThread.start();
        pThread.start();
        pThread1.start();
    }
}

class CThread extends Thread{
    private Consumer consumer;

    public CThread(Consumer consumer) {
        this.consumer = consumer;
    }

    @Override
    public void run(){
        while (true) {
            consumer.popService();
        }
    }
}

class PThread extends Thread{
    private Provider provider;

    public PThread(Provider provider){
        this.provider = provider;
    }

    @Override
    public void run() {
        while (true) {
            provider.pushService();
        }
    }
}

Lock的使用

ReentrantLock类的使用

  1. 使用ReentrantLock可以用来实现同步
lock.lock();
	lock.unlock();	// 一般写在finally当中,因为出现异常锁不会自动释放
  1. 使用Condition实现等待通知(还是应当注意signal()和signalAll()的使用,避免多对多等待通知时“ 假死 ”情况的出现
public class Service {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void await(){
        try {
            lock.lock();
            System.out.println("await时间为:" + System.currentTimeMillis());
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void signal(){
        try {
            lock.lock();
            System.out.println("signal时间为:" + System.currentTimeMillis());
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}
  1. 实现生产者 / 消费者模式:多对多打印
    服务类(这里需要注意的还是condition的signalAll()方法的使用
public class Service {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean hasValue = false;

    public void set(){
        try {
            lock.lock();
            while (hasValue == true){
                condition.await();
            }
            System.out.println("######");
            hasValue = true;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void get(){
        try {
            lock.lock();
            while (hasValue == false){
                condition.await();
            }
            System.out.println("*****");
            hasValue = false;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

线程类

public class Run {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA[] a = new ThreadA[2];
        ThreadB[] b = new ThreadB[2];
        for (int i = 0; i < 2; i++) {
            a[i] = new ThreadA(service);
            b[i] = new ThreadB(service);
            a[i].start();
            b[i].start();
        }
    }
}

class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        while (true) {
            service.set();
        }
    }
}

class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        while (true) {
            service.get();
        }
    }
}
  1. Lock的公平锁与非公平锁
    公平锁:获取锁的顺序大致上按线程加锁顺序来执行,FIFO(先进先出)
    非公平锁:抢占式获取锁,先来不一定获取得到锁,有可能会导致有些线程一直获取不到锁
// 设置公平锁与非公平锁
	boolean isFair = true;
	ReentrantLock lock = new ReentrantLock(isFair);
  1. 使用多个Condition实现顺序执行(这里可以使用signal()因为唤醒的是不同的condition)
public class Run {
    volatile private static int nextPrintWho = 1;
    private static ReentrantLock lock = new ReentrantLock();
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static Condition conditionC = lock.newCondition();

    public static void main(String[] args) {
        Thread threadA = new Thread(){
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 1){
                       conditionA.await();
                    }
                    nextPrintWho = 2;
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadA:" + (i + 1));
                    }
                    conditionB.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        };
        Thread threadB = new Thread(){
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 2){
                        conditionB.await();
                    }
                    nextPrintWho = 3;
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadB:" + (i + 1));
                    }
                    conditionC.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        };

        Thread threadC = new Thread(){
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 3){
                        conditionC.await();
                    }
                    nextPrintWho = 1;
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadC:" + (i + 1));
                    }
                    conditionA.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }

            }
        };

        Thread[] ta = new Thread[5];
        Thread[] tb = new Thread[5];
        Thread[] tc = new Thread[5];

        for (int i = 0; i < 5; i++) {
            ta[i] = new Thread(threadA);
            tb[i] = new Thread(threadB);
            tc[i] = new Thread(threadC);
            ta[i].start();
            tb[i].start();
            tc[i].start();
        }
    }
}

ReentrantReadWriteLock类的使用

ReentrantLock具有完全互斥排他效果,好处是通过同步保证了线程安全,缺点则是效率低下。因此,JDK推出了ReentrantReadWriteLock类,在某些不需要操作实例变量方法中,可以使用ReentrantReadWriteLock来提高效率。

读写锁ReentrantReadWriteLock具有以下特点:

  1. 读读共享
  2. 读写互斥
  3. 写读互斥
  4. 写写互斥

可以通过下面的代码去自行测试:

public class Service {

    private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public static void main(String[] args) {
        Service service = new Service();
        ThreadA ta = new ThreadA(service);
        ta.setName("A");
        ta.start();
        ThreadB tb = new ThreadB(service);
        tb.setName("B");
        tb.start();
    }

    synchronized public static void set() {
        try {
            readWriteLock.writeLock().lock();
            System.out.println("这是写锁" + Thread.currentThread().getName() +
                    System.currentTimeMillis());
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    public void get() {
        try {
            readWriteLock.readLock().lock();
            System.out.println("这是读锁" + Thread.currentThread().getName() +
                    System.currentTimeMillis());
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

class ThreadA extends Thread {
    private Service service;

    public ThreadA(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.set();
    }
}

class ThreadB extends Thread {
    private Service service;

    public ThreadB(Service service) {
        this.service = service;
    }

    @Override
    public void run() {
        service.set();
    }
}

总结

我认为本书主要讲了Synchronize和Lock的使用,等待通知机制的各种实现,还有一些零碎的线程方法,本文并没有进行罗列。本书还讲解了上述线程操作的基本姿势,但并没有特别的深入原理,不过这本书的配套书Java并发编程则是对这本书原理的详细讲解,也算是进阶。