Java并发编程系列文章
《一》多线程基础——Java线程与进程的基本概念
《二》多线程基础——Java线程入门类和接口
《三》多线程基础——Java线程组和线程优先级
《四》多线程基础——Java线程生命周期及转换
《五》多线程基础——Java线程间的通信(互斥与协作)
《六》实际应用——如何优雅的关闭线程
《七》实际应用——生产者与消费者模型
并发编程(多线程)一直以来都是程序员头疼的难题。曾经听别人总结过并发编程的第一原则,那就是不要写并发程序,哈哈哈。后来发现,这样能够显著提高程序响应和吞吐量的利器,哪还能忍得住不会用呢?
整理出《Java并发编程系列文章》,共计7篇,与君共勉。
在Java中,锁的概念都是基于对象的,所以我们⼜经常称它为对象锁。线程同步听着很高大上,cao,被吓了一跳,其实就是让线程之间按照⼀定的顺序执⾏。怎么实现呢?我们可以使⽤锁来实现它。
public class ObjectLock {private static Object lock = new Object();static class ThreadA implements Runnable {@Overridepublic void run() {synchronized (lock) {for (int i = 0; i < 100; i++) {System.out.println("Thread A " + i);}}}}static class ThreadB implements Runnable {@Overridepublic void run() {synchronized (lock) {for (int i = 0; i < 100; i++) {System.out.println("Thread B " + i);}}}}public static void main(String[] args) throws InterruptedException {new Thread(new ThreadA()).start();Thread.sleep(10);new Thread(new ThreadB()).start();}
}
这⾥声明了⼀个名字为 lock 的对象锁。我们在 ThreadA 和ThreadB 内需要同步的代码块⾥,都是⽤ synchronized 关键字加上了同⼀个对象锁 lock 。
根据线程和锁的关系,同⼀时间只有⼀个线程持有⼀个锁,那么线程B就会等线程A执⾏完成后释放 lock ,线程B才能获得锁 lock 。
这⾥在主线程⾥使⽤sleep⽅法睡眠了10毫秒,是为了防⽌线程B先得到锁。因为如果同时start,线程A和线程B都是出于就绪状态,操作系统可能会先让B运⾏。这样就会先输出B的内容,然后B执⾏完成之后⾃动释放锁,线程A再执⾏。
上⾯⼀种基于“锁”的⽅式,线程需要不断地去尝试获得锁,如果失败了,再继续尝试。这可能会耗费服务器资源。⽽等待/通知机制是另⼀种⽅式。
Java多线程的等待/通知机制是基于 Object 类的 wait() ⽅法和 notify() ,notifyAll() ⽅法来实现的。notify()⽅法会随机叫醒⼀个正在等待的线程,⽽notifyAll()会叫醒所有正在等待的线程。
例如:线程A持有了⼀个锁 lock 并开始执⾏,它可以使⽤ lock.wait() 让⾃⼰进⼊等待状态。这个时
候, lock 这个锁是被释放了的。线程B获得了 lock 这个锁并开始执⾏,它可以在某⼀时刻,使
⽤ lock.notify() ,叫醒之前释放 lock 锁并进⼊等待状态的线程A,注意,此时锁依然在线程B手中,只有当线程B执行完了,线程A才能竞争lock 锁。
public class WaitAndNotify {private static Object lock = new Object();static class ThreadA implements Runnable {@Overridepublic void run() {synchronized (lock) {for (int i = 0; i < 5; i++) {try {System.out.println("ThreadA: " + i);lock.notify();lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}lock.notify();}}}static class ThreadB implements Runnable {@Overridepublic void run() {synchronized (lock) {for (int i = 0; i < 5; i++) {try {System.out.println("ThreadB: " + i);lock.notify();lock.wait();} catch (InterruptedException e) {e.printStackTrace();}}lock.notify();}}}public static void main(String[] args) throws InterruptedException {new Thread(new ThreadA()).start();Thread.sleep(1000);new Thread(new ThreadB()).start();}
}
// 输出:
ThreadA: 0ThreadB: 0ThreadA: 1ThreadB: 1ThreadA: 2ThreadB: 2ThreadA: 3ThreadB: 3ThreadA: 4ThreadB: 4
在这个Demo⾥,线程A和线程B⾸先打印出⾃⼰需要的东⻄,然后使⽤ notify() ⽅法叫醒另⼀个正在等待的线程,然后⾃⼰使⽤ wait() ⽅法陷⼊等待并释放 lock 锁。
需要注意的是等待/通知机制使⽤的是使⽤同⼀个对象锁,如果你两个线程使 ⽤的是不同的对象锁,那它们之间是不能⽤等待/通知机制通信的。
等待后续补充
等待后续补充