文章目录
- 线程状态转移图
- thread.start() & thread.run() 的区别
- getId() 的作用
- 停止线程
- yield() 方法
- 实例变量非线程安全的例子
- synchronized
- volatile
- 线程间通信
- Lock
- 单例模式与多线程
- Concurrent集合
- ExecutorService线程池
-
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadExecutor
- newScheduledThreadPool
- ExecutorService方法介绍
-
- awaitTermination(long timeout, TimeUnit unit)
- invokeAll(Collection<? extends Callable> tasks)
- invokeAll(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)
- invokeAny(Collection<? extends Callable> tasks)
- invokeAny(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)
- shutdown()
- isShutdown()
- isTerminated()
- shutdownNow()
- submit(Callable task)
- submit(Runnable task)
- submit(Runnable task, T result)
- Future
- 参考资料
- 代码
线程状态转移图
thread.start() & thread.run() 的区别
示例
/**
* thread.start() 和 thread.run()的区别
*/
public class StartAndRun implements Runnable{
@Override
public void run() {
System.out.println("Thread name: " + Thread.currentThread().getName());
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StartAndRun());
System.out.println("-------thread.run()-------");
thread.run();
Thread.sleep(1000);
System.out.println("-------thread.start()-------");
thread.start();
}
}
代码:chapter2#StartAndRun
getId() 的作用
getId() 方法的作用是取得线程的唯一标识。
示例
public class GetId implements Runnable{
@Override
public void run() {
System.out.println("Run thread id: " + Thread.currentThread().getId());
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new GetId());
thread.start();
Thread.sleep(1000);
System.out.println("Main thread id: " + Thread.currentThread().getId());
}
}
代码:chapter3#GetId
停止线程
在Java中有3种方法可以终止正在运行的线程:
- 线程正常执行结束。
- 使用stop、suspend、resume强制终止线程。(这三个方法都是过期方法,不推荐使用)
- 使用interrupt方法中断线程。
interrupt() 方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。
isInterrupted()
isInterrupted() 方法测试线程是否已经是中断状态,但不清除状态标志。
示例
public class IsInterrupted implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new IsInterrupted());
System.out.println("Thread starting..");
thread.start();
Thread.sleep(1);
System.out.println("Thread interrupting..");
thread.interrupt();
System.out.println("Thread isInterrupt = " + thread.isInterrupted());
Thread.sleep(1000);
System.out.println("Thread isInterrupt = " + thread.isInterrupted());
}
}
代码:chapter4#Interrupted
yield() 方法
yield() 方法的作用是:放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
注释掉:Thread.yeild() 方法
示例
public class Yield implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("sub thread, i = " + i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Yield());
thread.start();
for (int i = 1; i <= 10; i++) {
System.out.println("Main thread, i = " + i);
// Thread.yield();
}
}
}
public class Yield implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("sub thread, i = " + i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Yield());
thread.start();
for (int i = 1; i <= 10; i++) {
System.out.println("Main thread, i = " + i);
Thread.yield();
}
}
}
代码:chapter5#Yield
实例变量非线程安全的例子
- 如果实例变量是:方法内部的私有变量,则不存在“非线程安全”的问题。这是方法内部的变量是私有的特性所造成的。
- 如果多个线程共同访问同一个对象的实例变量,则有可能出现“非线程安全”的问题。
示例
public class TestClass {
private int num = 0;
public void getTargetName(String targetName) throws InterruptedException {
if ("ThreadA".equals(targetName)) {
num = 100;
System.out.println("ThreadA is end!");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("ThreadB is end!");
Thread.sleep(1000);
}
System.out.println("num result is " + num);
}
public static void main(String[] args) {
TestClass testClass = new TestClass();
Thread threadA = new Thread(() -> {
try {
testClass.getTargetName("ThreadA");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadA.start();
Thread threadB = new Thread(() -> {
try {
testClass.getTargetName("ThreadB");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB.start();
}
}
代码:chapter6#TestClass
synchronized
关键字synchronized可以保证在同一刻,只有一个线程可以执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。
多个对象多个锁
多个对象多个锁,关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法(函数)当做锁。
示例
/**
* 创建两个SynchronizedOne对象,分别用两个线程调用。
* SynchronizedOne对象的方法加了关键字:synchronized,但是结果还是异步的
*/
public class SynchronizedOne {
private int num = 0;
synchronized public void getTargetName(String targetName) throws InterruptedException {
if ("ThreadA".equals(targetName)) {
num = 100;
System.out.println("ThreadA is end!");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("ThreadB is end!");
Thread.sleep(1000);
}
System.out.println("num result is " + num);
}
public static void main(String[] args) {
SynchronizedOne synchronizedOne = new SynchronizedOne();
Thread threadA = new Thread(() -> {
try {
synchronizedOne.getTargetName("ThreadA");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadA.start();
SynchronizedOne synchronizedTwo = new SynchronizedOne();
Thread threadB = new Thread(() -> {
try {
synchronizedTwo.getTargetName("ThreadB");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB.start();
}
}
代码:chapter7#SynchronizedOne
使用同一个对象后
示例
public class SynchronizedTwo {
private int num = 0;
synchronized public void getTargetName(String targetName) throws InterruptedException {
if ("ThreadA".equals(targetName)) {
num = 100;
System.out.println("ThreadA is end!");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("ThreadB is end!");
Thread.sleep(1000);
}
System.out.println("num result is " + num);
}
public static void main(String[] args) {
SynchronizedTwo synchronizedTwo = new SynchronizedTwo();
Thread threadA = new Thread(() -> {
try {
synchronizedTwo.getTargetName("ThreadA");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadA.start();
Thread threadB = new Thread(() -> {
try {
synchronizedTwo.getTargetName("ThreadB");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB.start();
}
}
代码:chapter7#SynchronizedTwo
脏读
线程A先持有对象A的锁,线程B可以以异步的方式调用对象A中的非synchronized类型的方法。若synchronized和非synchronized方法调用了同一个实例变量,则有可能出现脏读的情况。
示例
/**
* 主线程 启动 子线程 去修改username + password
* 在子线程修改的过程中(Thread.sleep(1000)),主线程读取对象中的username + password
* 则有几率出现脏读的情况
*/
public class SynchronizedThree {
private String userName = "admin";
private String password = "admin";
synchronized public void setValue(String userName, String password) throws InterruptedException {
this.userName = userName;
Thread.sleep(1000);
this.password = password;
System.out.println("setValue method -> userName = " + this.userName + ", password = " + this.password);
}
public void getValue() {
System.out.println("getValue method -> userName = " + this.userName + ", password = " + this.password);
}
public static void main(String[] args) throws InterruptedException {
SynchronizedThree synchronizedThree = new SynchronizedThree();
Thread thread = new Thread(() -> {
try {
synchronizedThree.setValue("root", "root");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
Thread.sleep(10);
synchronizedThree.getValue();
}
}
代码:chapter7#SynchronizedThree
两个方法都加上关键字synchronized
示例
public class SynchronizedFour {
private String userName = "admin";
private String password = "admin";
synchronized public void setValue(String userName, String password) throws InterruptedException {
this.userName = userName;
Thread.sleep(1000);
this.password = password;
System.out.println("setValue method -> userName = " + this.userName + ", password = " + this.password);
}
synchronized public void getValue() {
System.out.println("getValue method -> userName = " + this.userName + ", password = " + this.password);
}
public static void main(String[] args) throws InterruptedException {
SynchronizedFour synchronizedFour = new SynchronizedFour();
Thread thread = new Thread(() -> {
try {
synchronizedFour.setValue("root", "root");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
Thread.sleep(10);
synchronizedFour.getValue();
}
}
代码:chapter7#SynchronizedFour
synchronized锁重入
synchronized 方法/块的内部调用本类的其他synchronized 方法/块时,是永远可以得到锁的。父子类继承也符合synchronized锁重入的条件。
示例
/**
* Main & Sub类的方法都加上关键字synchronized
* Sub类循环调用Main的方法
*/
public class Main {
public int i = 10;
synchronized public void operateIMainMethod() throws InterruptedException {
i--;
System.out.println("Main print i = " + i);
Thread.sleep(1000);
}
}
public class Sub extends Main {
synchronized public void operateISubMethod() throws InterruptedException {
while (i > 0) {
i--;
System.out.println("Sub print i = " + i);
Thread.sleep(1000);
this.operateIMainMethod();
}
}
public static void main(String[] args) {
Sub sub = new Sub();
Thread thread = new Thread(() -> {
try {
sub.operateISubMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
}
代码:chapter8
synchronized同步语句块
示例
/**
* 加了关键字synchronized的代码块内的执行顺序是:同步的
* 没有加关键字synchronized的代码块内执行顺序是:异步的
*/
public class Task {
private String taskName;
public void logTaskName(String inputTaskName) throws InterruptedException {
System.out.println("Begin!");
Thread.sleep(1000);
synchronized (this) {
this.taskName = inputTaskName;
System.out.println("The task name result is " + this.taskName + ";The input task name is " + inputTaskName);
System.out.println("End!");
}
}
public static void main(String[] args) {
Task task = new Task();
Thread threadA = new Thread(() -> {
try {
task.logTaskName("A");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadA.start();
Thread threadB = new Thread(() -> {
try {
task.logTaskName("B");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB.start();
}
}
代码:chapter9#Task
静态同步synchronized 方法与synchronized(class) 代码块
- 关键字synchronized可以加在static静态方法上,对当前的 *.java 文件对应的class类进行持锁。
- synchronized static 和 synchronized(class) 的作用是一样的
- 关键字synchronized可以加在非static静态方法上,是给当前的对象上锁。
示例
/**
* 两个线程调用的是两个对象内的同步方法
* 得到的结果总是同步的
*/
public class Service {
synchronized public static void printA() throws InterruptedException {
System.out.println("Print A begin");
Thread.sleep(1000);
System.out.println("Print A end");
}
synchronized public static void printB() throws InterruptedException {
System.out.println("Print B begin");
System.out.println("Print B end");
}
public static void main(String[] args) {
Service service1 = new Service();
Service service2 = new Service();
Thread threadA = new Thread(() -> {
try {
service1.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadA.start();
Thread threadB = new Thread(() -> {
try {
service2.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB.start();
}
}
代码:chapter10#Service
去掉同步方法上的static
示例
/**
* 去掉同步方法上的static
* 得到的结果是异步的
* 这时候synchronized获得的锁是:对象锁
*/
public class Service2 {
synchronized public void printA() throws InterruptedException {
System.out.println("Print A begin");
Thread.sleep(1000);
System.out.println("Print A end");
}
synchronized public void printB() throws InterruptedException {
System.out.println("Print B begin");
System.out.println("Print B end");
}
public static void main(String[] args) {
Service2 service1 = new Service2();
Service2 service2 = new Service2();
Thread threadA = new Thread(() -> {
try {
service1.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadA.start();
Thread threadB = new Thread(() -> {
try {
service2.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadB.start();
}
}
代码:chapter10#Service2
死锁
死锁产生的条件是:多线程各自持有不同的锁,并互相视图获取对方个已持有的锁,导致双方各自无限等待。
避免死锁的方法是:多线程获取锁的顺序要一致。
示例
/**
* 分别调用同一个对象的同一个方法,根据传入的值不同,走不同的流程。
* 流程中互相调用对方的锁,双方都在等对方释放锁,最终导致死锁。
*/
public class DeadLock {
public Object lock1 = new Object();
public Object lock2 = new Object();
public void logTaskName(String taskName) throws InterruptedException {
/**
* 从lock1 切换到 lock2
*/
if ("A".equals(taskName)) {
synchronized (lock1) {
System.out.println("taskName = " + taskName);
Thread.sleep(3000);
synchronized (lock2) {
System.out.println("lock1 -> lock2");
}
}
}
/**
* 从lock2 切换到 lock1
*/
if ("B".equals(taskName)) {
synchronized (lock2) {
System.out.println("taskName = " + taskName);
Thread.sleep(3000);
synchronized (lock1) {
System.out.println("lock2 -> lock1");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
DeadLock deadLock = new DeadLock();
Thread threadA = new Thread(() -> {
try {
deadLock.logTaskName("A");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread threadB = new Thread(() -> {
try {
deadLock.logTaskName("B");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadA.start();
threadB.start();
}
}
代码:chapter12#DeadLock
volatile
关键字volatile 的主要作用是:使变量在多个线程间可见。强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。
- volatile只能修饰于变量
- volatile能保证数据的可见性,但不能保证原子性
关键字volatile 主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用。
参考资料
线程间通信
wait()
- 作用:使当前执行代码的线程进行等待,直到接到通知或被中断为止。
- 在调用 wait() 之前,线程必须获得该对象的对象级别锁,即:只能在同步方法或同步块中使用。
- wait() 方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。
- wait(long):当超过设定的时间还是没有其他线程唤醒此线程,则会自动唤醒。
notify()
- 作用是:用来通知那些等待该对象的对象锁的其他线程。
- 调用notify()之前,也需要获得该对象的对象级别锁。
- 调用notify()之后,当前线程不会马上释放该对象锁,呈wait状态的线程也不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,当前线程才会释放锁。
notifyAll()
- notifyAll()方法可以使所有正在等待队列中等待同一共享资源的“全部”线程从等待状态退出,进入可运行状态。
- 一般优先级最高的线程最先执行,
示例
public class WaitAndNotify {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
// wait()
Thread waitThread = new Thread(() -> {
try {
synchronized (object) {
System.out.println("wait() -> begin");
object.wait();
System.out.println("wait() -> end");
}
} catch (Exception e) {
e.printStackTrace();
}
});
// notify()
Thread notifyThread = new Thread(() -> {
try {
synchronized (object) {
System.out.println("notify() -> begin");
object.notify();
System.out.println("notify() -> end");
}
} catch (Exception e) {
e.printStackTrace();
}
});
waitThread.start();
notifyThread.start();
}
}
代码:chapter11#WaitAndNotify
当 interrupt() 遇到 wait()
当线程处于 wait() 状态时,调用线程对象的 interrupt() 方法,会出现 InterruptedException 异常。
示例
/**
* 先让线程调用wait()方法,接着再调用interrupt()方法。
*/
public class WaitAndInterrupted {
public Object lockObject = new Object();
public void waitMethod() throws InterruptedException {
synchronized (lockObject) {
System.out.println("begin wait..");
lockObject.wait();
System.out.println("end wait..");
}
}
public static void main(String[] args) throws InterruptedException {
WaitAndInterrupted waitAndInterrupted = new WaitAndInterrupted();
Thread thread = new Thread(() -> {
try {
waitAndInterrupted.waitMethod();
} catch (InterruptedException e) {
System.out.println("Throw InterruptedException...");
}
});
thread.start();
thread.interrupt();
}
}
代码:chapter13#WaitAndInterrupted
notify() 和 notifyAll()
notify() 只能随机唤醒“一个”线程。
notifyAll() 可以唤醒所有线程。
示例1:notify()
public class NotifyOneThread {
public void testNotifyOneThread(Object lock) throws InterruptedException {
synchronized (lock) {
System.out.println("begin wait.." + Thread.currentThread().getName());
lock.wait();
System.out.println("end wait.." + Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
NotifyOneThread notifyOneThread = new NotifyOneThread();
Object lock = new Object();
Thread threadOne = new Thread(() -> {
try {
notifyOneThread.testNotifyOneThread(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread threadTwo = new Thread(() -> {
try {
notifyOneThread.testNotifyOneThread(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread threadThree = new Thread(() -> {
try {
notifyOneThread.testNotifyOneThread(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread notifyThread = new Thread(() -> {
synchronized (lock) {
lock.notify();
// lock.notifyAll();
}
});
threadOne.start();
threadTwo.start();
threadThree.start();
Thread.sleep(1000);
notifyThread.start();
}
}
public class NotifyOneThread {
public void testNotifyOneThread(Object lock) throws InterruptedException {
synchronized (lock) {
System.out.println("begin wait.." + Thread.currentThread().getName());
lock.wait();
System.out.println("end wait.." + Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
NotifyOneThread notifyOneThread = new NotifyOneThread();
Object lock = new Object();
Thread threadOne = new Thread(() -> {
try {
notifyOneThread.testNotifyOneThread(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread threadTwo = new Thread(() -> {
try {
notifyOneThread.testNotifyOneThread(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread threadThree = new Thread(() -> {
try {
notifyOneThread.testNotifyOneThread(lock);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread notifyThread = new Thread(() -> {
synchronized (lock) {
// lock.notify();
lock.notifyAll();
}
});
threadOne.start();
threadTwo.start();
threadThree.start();
Thread.sleep(1000);
notifyThread.start();
}
}
代码:chapter14#NotifyOneThread
生产者/消费者
多生产者与多消费者导致的死锁
假死:所有线程都进入了等待状态。
主要原因:调用 notify() 方法时,唤醒的是同类。即:生产者唤醒生产者,消费者唤醒消费者。
解决方法:调用 notifyAll() 方法,将所有等待的线程都唤醒。
示例1:notify()
public class FakeDead {
public String value = "";
// 生产者
public void producer(Object lock) throws InterruptedException {
synchronized (lock) {
while (!value.equals("")) {
System.out.println("生产者: " + Thread.currentThread().getName() + " waiting☆");
lock.wait();
}
System.out.println("生产者: " + Thread.currentThread().getName() + " running☆");
value = System.currentTimeMillis() + " --- " + Thread.currentThread().getName();
lock.notify();
}
}
// 消费者
public void consumer(Object lock) throws InterruptedException {
synchronized (lock) {
while (value.equals("")) {
System.out.println("消费者: " + Thread.currentThread().getName() + " waiting★");
lock.wait();
}
System.out.println("消费者: " + Thread.currentThread().getName() + " running★");
value = "";
lock.notify();
}
}
public static void main(String[] args) {
Object lock = new Object();
FakeDead fakeDead = new FakeDead();
for (int i = 0; i < 2; i++) {
// 创建消费者线程,循环的调用consumer方法
Thread consumerThread = new Thread(() -> {
try {
while (true) {
fakeDead.consumer(lock);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 创建生产者线程,循环的调用producer方法
Thread producerThread = new Thread(() -> {
try {
while (true) {
fakeDead.producer(lock);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
consumerThread.setName("consumer - " + i);
producerThread.setName("producer - " + i);
consumerThread.start();
producerThread.start();
}
}
}
2个消费者 和 2个生产者 最后都进入了waiting状态了
代码:chapter15#FakeDead
示例2:notifyAll()
public class FakeDead2 {
public String value = "";
// 生产者
public void producer(Object lock) throws InterruptedException {
synchronized (lock) {
while (!value.equals("")) {
System.out.println("生产者: " + Thread.currentThread().getName() + " waiting☆");
lock.wait();
}
System.out.println("生产者: " + Thread.currentThread().getName() + " running☆");
value = System.currentTimeMillis() + " --- " + Thread.currentThread().getName();
lock.notifyAll();
}
}
// 消费者
public void consumer(Object lock) throws InterruptedException {
synchronized (lock) {
while (value.equals("")) {
System.out.println("消费者: " + Thread.currentThread().getName() + " waiting★");
lock.wait();
}
System.out.println("消费者: " + Thread.currentThread().getName() + " running★");
value = "";
lock.notifyAll();
}
}
public static void main(String[] args) {
Object lock = new Object();
FakeDead2 fakeDead = new FakeDead2();
for (int i = 0; i < 2; i++) {
// 创建消费者线程,循环的调用consumer方法
Thread consumerThread = new Thread(() -> {
try {
while (true) {
fakeDead.consumer(lock);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 创建生产者线程,循环的调用producer方法
Thread producerThread = new Thread(() -> {
try {
while (true) {
fakeDead.producer(lock);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
consumerThread.setName("consumer - " + i);
producerThread.setName("producer - " + i);
consumerThread.start();
producerThread.start();
}
}
}
代码:chapter15#FakeDead2
因条件判断引起的线程异常
例子:使用一个生产者向堆栈List对象中放入数据,而多个消费者从List堆栈中取出数据。List的最大容量是:1。
示例1:使用 if 判断条件
public class IfCondition {
private List list = new ArrayList(1);
/**
* 数组大小 = 1 时,线程释放资源,进入等待
* 数组大小 != 1 时, 数组大小 + 1
*/
public void push(Object lock) {
synchronized (lock) {
try {
if (list.size() == 1) {
lock.wait();
}
list.add("anyString = " + Math.random());
lock.notify();
System.out.println("Current thread name = " + Thread.currentThread().getName() + " push = " + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 数组大小 = 0 时,线程释放资源,进入等待
* 数组大小 != 0 时, 数组大小 - 1
*/
public void pop(Object lock) {
synchronized (lock) {
try {
if (list.size() == 0) {
lock.wait();
}
list.remove(0);
lock.notify();
System.out.println("Current thread name = " + Thread.currentThread().getName() + " pop = " + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 主线程
*
* @param args
*/
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
IfCondition ifCondition = new IfCondition();
/**
* 一个生产者
*/
Thread pushThread = new Thread(() -> {
while (true) {
ifCondition.push(lock);
}
});
pushThread.start();
Thread.sleep(1000);
/**
* 多个消费者
*/
for (int i = 0; i < 3; i++) {
Thread popThread = new Thread(() -> {
while (true) {
ifCondition.pop(lock);
}
});
popThread.setName("popThread - " + i);
popThread.start();
}
}
}
代码:chapter16#IfCondition
示例2:使用 while 判断条件
除了 if 条件 改成 while 条件外,lock.notify() 也要改成 lock.notifyAll()。不然,解决了异常的问题,还会遇到死锁的问题。
public class WhileCondition {
private List list = new ArrayList(1);
/**
* 数组大小 = 1 时,线程释放资源,进入等待
* 数组大小 != 1 时, 数组大小 + 1
*/
public void push(Object lock) {
synchronized (lock) {
try {
while (list.size() == 1) {
lock.wait();
}
list.add("anyString = " + Math.random());
lock.notifyAll();
System.out.println("Current thread name = " + Thread.currentThread().getName() + " push = " + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 数组大小 = 0 时,线程释放资源,进入等待
* 数组大小 != 0 时, 数组大小 - 1
*/
public void pop(Object lock) {
synchronized (lock) {
try {
while (list.size() == 0) {
lock.wait();
}
list.remove(0);
lock.notifyAll();
System.out.println("Current thread name = " + Thread.currentThread().getName() + " pop = " + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 主线程
*
* @param args
*/
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
WhileCondition ifCondition = new WhileCondition();
/**
* 一个生产者
*/
Thread pushThread = new Thread(() -> {
while (true) {
ifCondition.push(lock);
}
});
pushThread.start();
Thread.sleep(1000);
/**
* 多个消费者
*/
for (int i = 0; i < 3; i++) {
Thread popThread = new Thread(() -> {
while (true) {
ifCondition.pop(lock);
}
});
popThread.setName("popThread - " + i);
popThread.start();
}
}
}
代码:chapter16#WhileCondition
join()
在主线程创建并启动子线程后,如果子线程中要进行大量的耗时计算,主线程将会早于子线程结束。如果主线程需要等待子线程完成后完成,则可以使用:join() 方法。
join() 方法的作用是:使子线程正常执行线程任务,使得主线程进入阻塞,直到子线程执行完后再执行。
join(long)
方法join(long):设定等待的时间。
public class Join {
public void execute() throws InterruptedException {
System.out.println("Begin");
Thread.sleep(1000);
System.out.println("End");
}
public static void main(String[] args) throws InterruptedException {
Join join = new Join();
Thread thread = new Thread(() -> {
try {
join.execute();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
thread.join();
System.out.println("Main thread end.");
}
}
代码:chapter17#Join
ThreadLocal
ThreadLocal:用于存放属于每个线程自己的值。
public class ThreadLocalTest {
private static ThreadLocal threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
testMethod();
});
Thread threadB = new Thread(() -> {
testMethod();
});
threadA.setName("ThreadA");
threadB.setName("ThreadB");
threadA.start();
threadB.start();
}
private static void testMethod() {
System.out.println(Thread.currentThread().getName() + " beginning...");
threadLocal.set("My thread name is " + Thread.currentThread().getName());
try {
Thread.sleep(5000);
System.out.println("Test threadlocal in " + Thread.currentThread().getName() + "; My threadlocal value is " + threadLocal.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
代码:chapter18#ThreadLocalTest
Lock
ReentrantLock
使用Reentrantlock达到和Synchronized同样的效果。
public class ReentrantLockTest {
private Lock lock = new ReentrantLock();
public void methodA() {
try {
lock.lock();
System.out.println("Method A begin -> " + Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("Method A end -> " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void methodB() {
try {
lock.lock();
System.out.println("Method B begin -> " + Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("Method B end -> " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
for (int i = 0; i < 6; i++) {
Thread threadA = new Thread(() -> {
reentrantLockTest.methodA();
});
Thread threadB = new Thread(() -> {
reentrantLockTest.methodB();
});
threadA.setName("ThreadA-" + i);
threadB.setName("ThreadB-" + i);
threadA.start();
threadB.start();
}
}
}
代码:chapter19#ReentrantLockTest
Condition的await & signal
Reentrantlock 配合 Condition 后,也可以实现像 Synchronized 配合 wait & notify 的 等待 & 通知的功能。
不同点:
- 一个Lock对象中可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。
- notify() & notifyAll() 方法进行通知时,被通知的对象是由JVM随机选择的结果。
public class ConditionTest {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("Await begin time is " + new Date());
condition.await();
System.out.println("Await end time is " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() {
lock.lock();
System.out.println("Signal begin time is " + new Date());
condition.signal();
System.out.println("Signal end time is " + new Date());
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
ConditionTest conditionTest = new ConditionTest();
Thread threadAwait = new Thread(() -> {
conditionTest.await();
});
Thread threadSignal = new Thread(() -> {
conditionTest.signal();
});
threadAwait.start();
Thread.sleep(3000);
threadSignal.start();
}
}
到目前为止,condition的await() & signal()/signalAll() 实现的效果和 wait() & notify()/notifyAll() 一样。
代码:chapter20#ConditionTest
多个Condition
在同一个实例中,创建两个Condition对象,然后实现只通知其中某一个Condition对象。
public class ConditionTest2 {
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("Begin await A time is " + new Date() + "; Current thread name is " + Thread.currentThread().getName());
conditionA.await();
lock.unlock();
System.out.println("End await A time is " + new Date() + "; Current thread name is " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("Begin await B time is " + new Date() + "; Current thread name is " + Thread.currentThread().getName());
conditionB.await();
lock.unlock();
System.out.println("End await B time is " + new Date() + "; Current thread name is " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void signalA() {
lock.lock();
System.out.println("Begin signal A time is " + new Date());
conditionA.signalAll();
lock.unlock();
}
public void signalB() {
lock.lock();
System.out.println("Begin signal B time is " + new Date());
conditionB.signalAll();
lock.unlock();
}
/**
* 创建两个线程,两个Condition对象,进入wait状态
* 之后调用其中一个Condition对象的signalAll方法
* 查看最终结果是不是只有ConditionA对象会被唤醒
* @param args
*/
public static void main(String[] args) throws InterruptedException {
ConditionTest2 conditionTest2 = new ConditionTest2();
Thread threadA = new Thread(() -> {
conditionTest2.awaitA();
});
Thread threadB = new Thread(() -> {
conditionTest2.awaitB();
});
threadA.setName("ThreadA");
threadB.setName("ThreadB");
threadA.start();
threadB.start();
Thread.sleep(1000);
conditionTest2.signalA();
}
}
结果是:只有ConditionA被唤醒了,ConditionB依旧处于wait状态。
代码:chapter20#ConditionTest2
公平锁与非公平锁
Lock分为“公平锁”和“非公平锁”
- 公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来分配的,先到先得。
- 非公平锁:就是线程获得锁的机会是随机的,这个可能会造成某些线程一直拿不到锁。
示例1:公平锁
public class ConditionTest4 {
// 注意这里创建Lock对象时,传入的参数是:true
private Lock lock = new ReentrantLock(true);
public void serviceMethod() {
lock.lock();
System.out.println("Current Thread: " + Thread.currentThread().getName() + " is locking..");
lock.unlock();
}
public static void main(String[] args) {
ConditionTest4 conditionTest4 = new ConditionTest4();
// 创建10个线程
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
System.out.println("Current Thread: " + Thread.currentThread().getName() + " is running..");
conditionTest4.serviceMethod();
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter20#ConditionTest3
示例2:非公平锁
public class ConditionTest4 {
// 注意这里创建Lock对象时,传入的参数是:false
private Lock lock = new ReentrantLock(false);
public void serviceMethod() {
lock.lock();
System.out.println("Current Thread: " + Thread.currentThread().getName() + " is locking..");
lock.unlock();
}
public static void main(String[] args) {
ConditionTest4 conditionTest4 = new ConditionTest4();
// 创建10个线程
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
System.out.println("Current Thread: " + Thread.currentThread().getName() + " is running..");
conditionTest4.serviceMethod();
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter20#ConditionTest4
getHoldCount()
getHoldCount():查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
public class GetHoldCount {
private ReentrantLock lock = new ReentrantLock();
public void serviceMethod() {
lock.lock();
System.out.println("ServiceMethod getHoldCount = " + lock.getHoldCount());
serviceMethod2();
lock.unlock();
}
public void serviceMethod2() {
lock.lock();
System.out.println("ServiceMethod2 getHoldCount = " + lock.getHoldCount());
serviceMethod3();
lock.unlock();
}
public void serviceMethod3() {
lock.lock();
System.out.println("ServiceMethod3 getHoldCount = " + lock.getHoldCount());
lock.unlock();
}
public static void main(String[] args) {
GetHoldCount getHoldCount = new GetHoldCount();
getHoldCount.serviceMethod();
}
}
代码:chapter20#GetHoldCount
getQueueLength()
getQueueLength():返回正等待获取某锁的线程估计数。
public class GetQueueLength {
private ReentrantLock lock = new ReentrantLock();
public void serviceMethod() {
try {
lock.lock();
System.out.println("Thread: " + Thread.currentThread().getName() + " is locking...");
Thread.sleep(3000);
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
GetQueueLength getQueueLength = new GetQueueLength();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
getQueueLength.serviceMethod();
});
thread.setName("Thread-" + i);
thread.start();
}
Thread.sleep(1000);
System.out.println("GetQueueLength = " + getQueueLength.lock.getQueueLength());
}
}
getWaitQueueLength(Condition condition)
getWaitQueueLength(Condition condition):返回等待与此锁相关的给定条件Condition的线程估计数。
eg:
有5个线程,每个线程都执行了同一个condition对象的 await() 方法,则调用getWaitQueueLength(Condition condition)方法时,返回的值为:5。
/**
* 创建5个线程,依次进入wait状态
*/
public class GetWaitQueueLength {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void serviceMethod() {
try {
lock.lock();
System.out.println("Thread: " + Thread.currentThread().getName() + " is locking..");
condition.await();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void signalMethod() {
lock.lock();
System.out.println("GetWaitQueueLength = " + lock.getWaitQueueLength(condition));
condition.signal();
System.out.println("After signal(), GetWaitQueueLength = " + lock.getWaitQueueLength(condition));
}
public static void main(String[] args) throws InterruptedException {
GetWaitQueueLength getWaitQueueLength = new GetWaitQueueLength();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
getWaitQueueLength.serviceMethod();
});
thread.setName("Thread-" + i);
thread.start();
}
Thread.sleep(2000);
getWaitQueueLength.signalMethod();
}
}
tryLock()
作用:仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。
public class TryLock {
private ReentrantLock lock = new ReentrantLock();
public void tryLockMethod() {
if (lock.tryLock()) {
System.out.println(Thread.currentThread().getName() + " 获得锁");
} else {
System.out.println(Thread.currentThread().getName() + " 未获得锁");
}
}
public static void main(String[] args) {
TryLock tryLock = new TryLock();
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(() -> {
tryLock.tryLockMethod();
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter20#TryLock
tryLock(long timeout, TimeUnit unit)
作用是:如果锁定在给定的等待时间内没有被另一个线程保持,且当前线程未中断,则获取该锁定。
awaitUninterruptibly()
当使用condition.await()时,线程调用interrupt()后,会抛出异常。
当使用condition.awaitUninterruptibly()时,则不会抛出异常。
public class AwaitUninterruptibly {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void awaitMethod() {
try {
lock.lock();
System.out.println("Before waiting..");
condition.await();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void awaitUninterruptibly() {
lock.lock();
System.out.println("Before waiting...");
condition.awaitUninterruptibly();
lock.unlock();
}
public static void main(String[] args) {
AwaitUninterruptibly awaitUninterruptibly = new AwaitUninterruptibly();
Thread thread1 = new Thread(() -> {
awaitUninterruptibly.awaitUninterruptibly();
});
thread1.start();
thread1.interrupt();
Thread thread = new Thread(() -> {
awaitUninterruptibly.awaitMethod();
});
thread.start();
thread.interrupt();
}
}
代码:chapter20#AwaitUninterruptibly
ReentrantReadWriteLock类
之前使用的ReentrantLock具有完全互斥排他的效果,即同一时间只有一个先线程可以执行lock.lock()后面的代码。这样虽然保证了实例变量的线程安全性,但效率却非常低下。
ReentrantReadWriteLock提供了两种锁:共享锁 & 排他锁。根据需要使用不同的锁,以提升代码的运行速度。
- 读 + 读(共享锁)
- 读 + 写(排他锁)
- 写 + 写(排他锁)
读 + 读(共享锁)
public class ReadAndRead {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void readMethod() throws InterruptedException {
lock.readLock().lock();
System.out.println("获得读锁 -> " + Thread.currentThread().getName());
Thread.sleep(1000);
lock.readLock().unlock();
}
public static void main(String[] args) {
ReadAndRead readAndRead = new ReadAndRead();
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(() -> {
try {
readAndRead.readMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter21#ReadAndRead
写 + 写(排他锁)
public class WriteAndWrite {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void writeMethod() throws InterruptedException {
lock.writeLock().lock();
System.out.println("获得写锁 -> " + Thread.currentThread().getName() + " " + new Date());
Thread.sleep(5000);
lock.writeLock().unlock();
}
public static void main(String[] args) {
WriteAndWrite writeAndWrite = new WriteAndWrite();
for (int i = 0; i < 2; i++) {
Thread thread = new Thread(() -> {
try {
writeAndWrite.writeMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter21#WriteAndWrite
读 + 写(排他锁)
public class WriteAndRead {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void writeMethod() throws InterruptedException {
lock.writeLock().lock();
System.out.println("获得写锁 -> " + Thread.currentThread().getName() + " " + new Date());
Thread.sleep(5000);
lock.writeLock().unlock();
}
public void readMethod() {
lock.readLock().lock();
System.out.println("获得读锁 -> " + Thread.currentThread().getName() + " " + new Date());
lock.readLock().unlock();
}
public static void main(String[] args) {
WriteAndRead writeAndRead = new WriteAndRead();
Thread threadWrite = new Thread(() -> {
try {
writeAndRead.writeMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threadWrite.setName("Thread-Write");
threadWrite.start();
Thread threadRead = new Thread(() -> {
writeAndRead.readMethod();
});
threadRead.setName("Thread-Read");
threadRead.start();
}
}
代码:chapter21#WriteAndRead
StampedLock
对于 ReentrantReadWriteLock 而言,只有读 + 读 是共享锁的,其他的都是互斥锁。
Java 8引入了新的读写锁,StampedeLock。不同之处在于:StampedeLock 在读的过程中允许写锁后写入。存在的问题是:之前读的数据可能会不一致,所以需要额外的操作来判断在读的过程中是否有写的操作。
public class ReadAndWrite {
private final StampedLock stampedLock = new StampedLock();
private String name = "name1";
public void writeName(String name) {
// 获取写锁
long stamp = stampedLock.writeLock();
this.name = name;
stampedLock.unlockWrite(stamp);
}
public void readName() throws InterruptedException {
// 获得一个乐观锁,得到一个版本号
long stamp = stampedLock.tryOptimisticRead();
String tempName = this.name;
// 让读线程睡0.1s,让写操作进行
Thread.sleep(1000);
// 判断在读的过程中是否有写入的操作,如果版本号检验失败,即:读的期间值发生了改变,则通过悲观锁进行读操作
if (!stampedLock.validate(stamp)) {
// 获取一个悲观锁,读锁
stamp = stampedLock.readLock();
tempName = this.name;
stampedLock.unlockRead(stamp);
}
System.out.println("Name = " + tempName);
}
public static void main(String[] args) {
ReadAndWrite readAndWrite = new ReadAndWrite();
Thread readThread = new Thread(() -> {
try {
readAndWrite.readName();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
readThread.start();
Thread writeThread = new Thread(() -> {
readAndWrite.writeName("name2");
});
writeThread.start();
}
}
区别
- 与 ReadWriteLock 相比,StampedLock 的代码更加复杂
- StampedLock 是不可重入锁,不能再一个线程中反复获取同一个锁
单例模式与多线程
饿汉模式
饿汉模式:在类加载时就完成了初始化,项目启动时比较慢。但是是线程安全的。
/**
* 多个线程获取Hungry对象
* 对象的HashCode是一致的
*/
public class Hungry {
private static Hungry hungry = new Hungry();
public static Hungry getInstance() {
return hungry;
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " -> " + Hungry.getInstance().hashCode());
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter22#Hungry
懒汉模式
懒汉模式 – 非线程安全版
/**
* 懒汉模式 - 非线程安全版
* 在new LazyPattern()前,让线程睡眠1s
* 导致其他线程都能够进到 lazyPattern == null 的分支中,得到不同的LazyPattern对象
*/
public class LazyPattern {
private static LazyPattern lazyPattern;
private LazyPattern() {
}
public static LazyPattern getInstance() throws InterruptedException {
if (lazyPattern == null) {
Thread.sleep(1000);
lazyPattern = new LazyPattern();
}
return lazyPattern;
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " -> " + LazyPattern.getInstance().hashCode());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter22#LazyPattern
懒汉模式 – 方法锁
在方法上加synchronized关键字
/**
* 懒汉模式 - 极端线程安全版
* 直接给getInstance()方法加锁
* 即使后面对象已经被实例化了,在获取对象时,都需要获取锁、释放锁的操作
*/
public class LazyPattern2 {
private static LazyPattern2 lazyPattern;
private LazyPattern2() {
}
public synchronized static LazyPattern2 getInstance() {
if (lazyPattern == null) {
lazyPattern = new LazyPattern2();
}
return lazyPattern;
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " -> " + LazyPattern2.getInstance().hashCode());
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter22#LazyPattern2
懒汉模式 – 代码块双重判断锁
/**
* 懒汉模式 - 双重判断锁
*/
public class LazyPattern3 {
private static LazyPattern3 lazyPattern;
private LazyPattern3() {
}
/**
* 假设:
* 线程A获取到锁,并且创建对象:LazyPattern3
* 在线程A创建对象的过程中(未创建完)
* 线程B判断第一个:lazyPattern == null,得到的结果为:False
* 则:线程B获取到了线程A创建的不完整的LazyPattern3对象
* @return
*/
public static LazyPattern3 getInstance() {
if (lazyPattern == null) {
synchronized (LazyPattern3.class) {
if (lazyPattern == null) {
lazyPattern = new LazyPattern3();
}
}
}
return lazyPattern;
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " -> " + LazyPattern3.getInstance().hashCode());
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter22#LazyPattern3
懒汉模式 – 代码块双重判断锁 + volatile
/**
* 懒汉模式 - 双重判断锁 + volatile
* 关于为什么加上:volatile后,可以解决双重判断锁的问题
* 可以参考:https://www.cnblogs.com/jing-an-feng-shao/p/10275001.html
*/
public class LazyPattern4 {
private static volatile LazyPattern4 lazyPattern;
private LazyPattern4() {
}
public static LazyPattern4 getInstance() {
if (lazyPattern == null) {
synchronized (LazyPattern4.class) {
if (lazyPattern == null) {
lazyPattern = new LazyPattern4();
}
}
}
return lazyPattern;
}
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " -> " + LazyPattern4.getInstance().hashCode());
});
thread.setName("Thread-" + i);
thread.start();
}
}
}
代码:chapter22#LazyPattern4
使用enum枚举类实现单例模式
在使用枚举类时,构造方法会被自动调用,可以通过这个特性实现单例模式。
public class EnumSingleton {
public enum InternalEnumSingleton {
readAndReadObject;
private ReadAndRead readAndRead;
private InternalEnumSingleton() {
System.out.println("创建EnumSingleton对象");
readAndRead = new ReadAndRead();
}
public ReadAndRead getReadAndRead() {
return readAndRead;
}
}
public static ReadAndRead getReadAndRead() {
return InternalEnumSingleton.readAndReadObject.getReadAndRead();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
System.out.println(EnumSingleton.getReadAndRead().hashCode());
});
thread.start();
}
}
}
代码:chapter22#EnumSingleton
Concurrent集合
接口 | 非线程安全 | 线程安全 |
---|---|---|
List | ArrayList | CopyOnWriteArrayList |
Map | HashMap | ConcurrentHashMap |
Set | HashSet / TreeSet | CopyOnWriteArraySet |
Queue | ArrayDeque / LinkedList | ArrayBlockingQueue / LinkedBlockingQueue |
Deque | ArrayDeque / LinkedList | LinkedBlockingDeque |
- 使用java.util.concurrent包提供的线程安全的并发集合可以大大简化多线程编程
- 多线程同时读写并发集合是安全的
- 尽量使用Java标准库提供的并发集合,避免自己编写同步代码
ExecutorService线程池
newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
public class CachedThreadPoolTest {
public static void main(String[] args) {
ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
cacheThreadPool.execute(new Thread(() -> {
System.out.println("Thread name = " + Thread.currentThread().getName());
}));
}
cacheThreadPool.shutdown();
}
}
代码:chapter23#CachedThreadPoolTest
newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
public class FixedThreadPool {
public static void main(String[] args) {
// 创建定长的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
// 遍历20次,线程数应在 ≤ 5
for (int i = 0; i < 20; i++) {
executorService.execute(new Thread(() -> {
System.out.println("Thread name = " + Thread.currentThread().getName());
}));
}
executorService.shutdown();
}
}
代码:chapter23#FixedThreadPoolTest
newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
public class SingleThreadExecutorTest {
public static void main(String[] args) {
// 创建单线程线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
// 遍历10次,每次遍历的线程池应是同一个
for (int i = 0;i < 10; i++) {
executorService.execute(new Thread(() -> {
System.out.println("Thread name = " + Thread.currentThread().getName());
}));
}
executorService.shutdown();
}
}
代码:chapter23#SingleThreadExecutorTest
newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行
一次性任务,在指定延迟时间后执行一次(.schedule)
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
// 一次性任务,在指定延迟时间后执行一次
scheduledExecutorService.schedule(new Thread(() -> {
System.out.println("schedule method -> " + Thread.currentThread().getName());
}), 1, TimeUnit.SECONDS);
定时任务,每x秒执行一次(.scheduleAtFixedRate)
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
/**
* initialDelay: 多少时间后执行
* period: 间隔时间
* 即:5秒后执行,每3秒执行一次
*/
System.out.println("当前时间 -> " + new Date());
scheduledExecutorService.scheduleAtFixedRate(new Thread(() -> {
System.out.println("scheduleAtFixedRate method -> " + Thread.currentThread().getName() + "; 目前时间为:" + new Date());
}), 5, 3, TimeUnit.SECONDS);
代码:chapter23#ScheduledThreadPoolTest
定时任务,每x秒执行一次,线程任务执行时间大于x秒后的现象(.scheduleAtFixedRate)
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
/**
* initialDelay: 多少时间后执行
* period: 间隔时间
* 即:5秒后执行,每3秒执行一次
*
* 设定线程执行的任务所花费的时间大于3秒,那么线程的执行情况应该是怎么样的?
*/
System.out.println("当前时间 -> " + new Date());
scheduledExecutorService.scheduleAtFixedRate(new Thread(() -> {
try {
Thread.sleep(4000);
System.out.println("scheduleAtFixedRate method -> " + Thread.currentThread().getName() + "; 目前时间为:" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}), 5, 3, TimeUnit.SECONDS);
代码:chapter23#ScheduledThreadPoolTest
定时任务,每x秒执行一次,线程任务执行时间大于x秒后的现象(.scheduleWithFixedDelay)
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
/**
* initialDelay: 多少时间后执行
* period: 间隔时间
* 即:5秒后执行,每3秒执行一次
*
* 设定线程执行的任务所花费的时间大于3秒,那么线程的执行情况应该是怎么样的?
*/
System.out.println("当前时间 -> " + new Date());
scheduledExecutorService.scheduleWithFixedDelay(new Thread(() -> {
try {
Thread.sleep(4000);
System.out.println("scheduleAtFixedRate method -> " + Thread.currentThread().getName() + "; 目前时间为:" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}), 5, 3, TimeUnit.SECONDS);
代码:chapter23#ScheduledThreadPoolTest
ExecutorService方法介绍
awaitTermination(long timeout, TimeUnit unit)
在所有任务执行了shundown() 或 timeout 或 interrupted()之前,都会一直阻塞着。
public class AwaitTerminationTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
// 线程任务在2s后执行完成
executorService.execute(new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println("Thread name = " + Thread.currentThread().getName() + "; Current time = " + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
/**
* 启动线程后,立刻执行shutdown()
* 但是这时候,线程是还没有执行完成的
*/
executorService.shutdown();
if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) {
System.out.println("Time1, awaitTermination = false, 线程未执行完!" );
} else {
System.out.println("Time1, awaitTermination = true, 线程已执行完!" );
}
// 主线程等待子线程执行完
Thread.sleep(2000);
if (executorService.awaitTermination(1, TimeUnit.SECONDS)) {
System.out.println("Time2, awaitTermination = true, 线程已执行完!" );
} else {
System.out.println("Time2, awaitTermination = false, 线程未执行完!" );
}
}
}
代码:chapter24#AwaitTerminationTest
invokeAll(Collection<? extends Callable> tasks)
执行给定的任务,当所有任务完成时,返回其执行结果
/**
* 实现Callable<String>接口,并且返回String类型的结果,返回结果具体情况具体分析
*/
public class CallableClass implements Callable<String> {
String name;
public CallableClass(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
return "Name = " + this.name;
}
}
public class InvokeAllTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(new CallableClass("A"));
tasks.add(new CallableClass("B"));
List<Future<String>> futures = executorService.invokeAll(tasks);
for (Future<String> future : futures) {
if (future.isDone() && !future.isCancelled()) {
System.out.println(future.get());
}
}
}
}
代码:chapter24#InvokeAllTest
invokeAll(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)
执行给定的任务,当所有任务完成或者超过了设定的时间时,返回其执行结果
/**
* 实现Callable<String>接口,并且返回String类型的结果,返回结果具体情况具体分析
*/
public class CallableClass implements Callable<String> {
String name;
public CallableClass(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
String name = this.name;
if (this.name.equals("B")) {
Thread.sleep(2000);
}
return "Name = " + this.name;
}
}
/**
* name = A时,瞬间执行完任务
* name = B时,需要等待2s
* 我们invokeAll设定的时间在1s
* 所以,最终得到的结果只有name = A
*/
public class InvokeAllTest2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(new CallableClass("A"));
tasks.add(new CallableClass("B"));
List<Future<String>> futures = executorService.invokeAll(tasks, 1, TimeUnit.SECONDS);
for (Future<String> future : futures) {
if (future.isDone() && !future.isCancelled()) {
System.out.println(future.get());
}
}
executorService.shutdown();
}
}
代码:chapter24#InvokeAllTest2
invokeAny(Collection<? extends Callable> tasks)
执行给定的任务,返回其中一个最先成功完成的任务的结果
/**
* 实现Callable<String>接口,并且返回String类型的结果,返回结果具体情况具体分析
*/
public class CallableClass implements Callable<String> {
String name;
public CallableClass(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
String name = this.name;
if (this.name.equals("A")) {
Thread.sleep(2000);
}
return "Name = " + this.name;
}
}
/**
* 因为name = A的任务需要等待2s的时间
* 所以B会先于A执行完
* 最终得到的结果是:Name = B
*/
public class InvokeAny {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(new CallableClass("A"));
tasks.add(new CallableClass("B"));
tasks.add(new CallableClass("C"));
String futureResult = executorService.invokeAny(tasks);
System.out.println("Result = " + futureResult);
}
}
代码:chapter24#InvokeAnyTest
invokeAny(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)
执行给定的任务,如果在超时之前完成任务,则返回任务中的结果。
任务超时的话,则会抛出Exception。
/**
* 实现Callable<String>接口,并且返回String类型的结果,返回结果具体情况具体分析
*/
public class CallableClass implements Callable<String> {
String name;
public CallableClass(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
String name = this.name;
if (this.name.equals("B")) {
Thread.sleep(2000);
}
return "Name = " + this.name;
}
}
/**
* 执行name = B的任务需要2s的时间
* 我们期待在1s的时间拿到任务的结果
* 此时会抛出Exception
*/
public class InvokeAny2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(new CallableClass("B"));
String futureResult = null;
try {
futureResult = executorService.invokeAny(tasks, 1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
futureResult = "Exception";
}
System.out.println(futureResult);
executorService.shutdown();
}
}
代码:chapter24#InvokeAnyTest2
shutdown()
有序关闭之前触发的任务,不处理后续新加的任务。
isShutdown()
如果此执行程序已关闭,则返回true。
/**
* 执行任务,任务完成时间需要:2s
* 在调用shutdown()前后,查看isShutdown()的值的前后变化
*/
public class IsShutdownTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Thread(() -> {
try {
System.out.println("Sub thread is running..");
Thread.sleep(2000);
System.out.println("Sub thread is down..");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
Thread.sleep(1000);
System.out.println("Before execute shutdown method, " + "isShutdown = " + executorService.isShutdown());
executorService.shutdown();
System.out.println("After execute shutdown method, " + "isShutdown = " + executorService.isShutdown());
}
}
代码:chapter24#IsShutdownTest
isTerminated()
isTerminated()需要所有的任务都结束了,才会返回true。
isShutdown()只要程序调用了shundown()后,就会返回true。
/**
* 执行任务,任务完成时间需要:2s
* 在调用shutdown()前后,查看isTerminated()的值的前后变化
*/
public class IsTerminatedTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Thread(() -> {
try {
System.out.println("Sub thread is running..");
Thread.sleep(2000);
System.out.println("Sub thread is down..");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
System.out.println("Before execute shutdown method, " + "isTerminated = " + executorService.isTerminated());
executorService.shutdown();
System.out.println("After execute shutdown method, but tasks still running, " + "isTerminated = " + executorService.isTerminated());
Thread.sleep(3000);
System.out.println("After execute shutdown method and tasks is down, " + "isTerminated = " + executorService.isTerminated());
}
}
代码:chapter24#IsTerminatedTest
shutdownNow()
尝试停止所有正在执行的任务,中止正在等待的任务的处理,并返回正在等待执行的任务的列表
/**
* 给定一个单线程
* 提交5次执行的任务
* 任务中:
* (1)如果 随机数 % 2 == 0,则线程进入睡眠5s
* (2)如果 随机数 % 2 != 0,则线程立刻执行完毕
* 主线程中,睡眠1s后,查看待执行的线程数量
*/
public class ShutdownNowTest {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
executorService.submit(new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " is running..");
int result = (int) (Math.random() * 10);
System.out.println(Thread.currentThread().getName() + " result = " + result);
if (result % 2 == 0) {
System.out.println(Thread.currentThread().getName() + " is sleeping..");
Thread.sleep(5000);
}
System.out.println(Thread.currentThread().getName() + " is down..");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " throws exception");
}
}));
}
Thread.sleep(1000);
List<Runnable> runnables = executorService.shutdownNow();
System.out.println("Waiting list size = " + runnables.size());
}
}
总共有5个任务要执行,前2个任务都是瞬间完成了,第3个任务进入了睡眠,第4、5个任务处于待执行的阶段。
所以结果是:2个任务执行完成,1个任务进入睡眠,2个任务待执行。
在执行了shutdownNow()后,进入睡眠的线程任务会被中断,并抛出异常
代码:chapter24#ShutdownNowTest
submit(Callable task)
提交待执行的任务,并且得到任务返回的结果。
public class SubmitTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
// submit(Callable<T> task)
for (int i = 0; i < 5; i++) {
Future<String> future = executorService.submit(new CallableClass("A"));
System.out.println(future.get());
}
}
}
代码:chapter24#SubmitTest
submit(Runnable task)
提交一个Runnable任务用于执行,并返回一个代表该任务的Future。(不知道这个有什么作用)
future.get() = null
public class SubmitTest2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
// submit(Runnable task)
for (int i = 0; i < 5; i++) {
Future future = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("TEST");
}
});
System.out.println(future);
}
}
}
代码:chapter24#SubmitTest2
submit(Runnable task, T result)
同上,但是可以自己指定返回的结果是什么。
future.get() = result
public class SubmitTest3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
// submit(Runnable task, T result)
for (int i = 0; i < 5; i++) {
Future future = executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("TEST");
}
}, "I'm Result");
System.out.println(future.get());
}
}
}
代码:chapter24#SubmitTest3
Future
Future表示异步执行结果。Future提供了一些方法,以用于查看任务是否执行完成。
- 在任务完成后,可以通过get()的方式得到返回的结果
- 可以用cancel()的方式取消任务
- 任务一旦完成后,将不能够再被取消掉
get()
将会阻塞线程,直到拿到返回值或者抛出异常
get(long timeout, TimeUnit unit)
在指定的时间内拿不到返回值,则抛出java.util.concurrent.TimeoutException异常
/**
* 在符合this.number % 2 == 0条件下
* 线程将会睡眠5s
*/
public class CallableTestClass implements Callable<String> {
int number;
public CallableTestClass(int number) {
this.number = number;
}
@Override
public String call() throws Exception {
if (this.number % 2 == 0) {
Thread.sleep(5000);
}
return "Current thread name = " + Thread.currentThread().getName();
}
}
/**
* 设定get(long timeout, TimeUnit unit)在1s中获取任务返回值,否则抛出异常
*/
public class FutureGetTest {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 2; i++) {
CallableTestClass callableTestClass = new CallableTestClass(i);
Future future = executorService.submit(callableTestClass);
System.out.println("Method get(long timeout, TimeUnit unit) -> " + future.get(1, TimeUnit.SECONDS));
System.out.println("Method get() -> " + future.get());
}
}
}
代码:chapter25#FutureGetTest
参考资料
书:《Java多线程编程核心技术》— 高洪岩
Interface ExecutorService
廖雪峰的多线程学习资料
Java 四种线程池
Java Platform SE 7
Class CompletableFuture
CompletableFuture
Java Fork/Join 框架
代码
https://gitee.com/Protector_hui/concurrency-study.git
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/77905.html