Java多线程编程笔记
- synchronized
- synchronized同步方法
- synchronized同步代码块
- 小结
- 生产者 / 消费者模式实现
- 一生产一消费:操作值
- 多生产多消费:操作值
- 多生产多消费:操作栈
- Lock的使用
- ReentrantLock类的使用
- ReentrantReadWriteLock类的使用
- 总结
Java多线程编程核心技术
链接:https://pan.baidu.com/s/1waW7B9PTmkNMS-qkh6LNKg 提取码:cm4i
synchronized
synchronized同步方法
- 方法内的变量为线程安全的(因为局部变量在栈中,又因为栈是线程私有的,线程安全)
- 实例变量非线程安全(实例对象存在堆中,堆是线程共享区域,非线程安全)
- 多个对象会有多个锁
- synchronized锁可重入性(持有锁A后,可调用其它需要持有锁A的方法,若不可重入则会死锁)
- 出现异常时,锁会自动释放
- 同步不能被子类继承
synchronized同步代码块
- synchronized方法的弊端(当锁住的方法执行时间特别长时,等待该锁的其他线程就需要等待较长时间,推荐使用同步代码块锁关键代码,也就是需要同步的某一块代码)
- 使用String对象做监视器时,应着重注意(String对象存在于常量池或堆中)
- 锁对象的改变(对于锁对象而言,只要这个对象没变,即使修改了它的属性,结果还是同步的。例如对于一个student对象而言,当其作为锁对象存在时,修改它的学号姓名都没有问题,结果依旧会是同步的)
- 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类的使用
- 使用ReentrantLock可以用来实现同步
lock.lock();
lock.unlock(); // 一般写在finally当中,因为出现异常锁不会自动释放
- 使用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();
}
}
}
- 实现生产者 / 消费者模式:多对多打印
服务类(这里需要注意的还是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();
}
}
}
- Lock的公平锁与非公平锁
公平锁:获取锁的顺序大致上按线程加锁顺序来执行,FIFO(先进先出)
非公平锁:抢占式获取锁,先来不一定获取得到锁,有可能会导致有些线程一直获取不到锁
// 设置公平锁与非公平锁
boolean isFair = true;
ReentrantLock lock = new ReentrantLock(isFair);
- 使用多个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具有以下特点:
- 读读共享
- 读写互斥
- 写读互斥
- 写写互斥
可以通过下面的代码去自行测试:
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并发编程则是对这本书原理的详细讲解,也算是进阶。