主要内容:
1.单例模式及应用实例
2、多线程的通信
3、线程的五种状态
4、简单工厂设计模式
5、工厂方法模式
一、多线程的通信:
1.1 线程的五种状态
新建:创建线程对象。
就绪:线程对象启动了,但是还没有获取到CPU的执行权。
运行:获取到CPU的执行权。
阻塞:已经准备就绪的、但是还没有执行权。
死亡:代码执行完毕,线程消亡。
1.2 两个线程之间的通信:
1、什么时候需要通信:
如果是多个线程并发执行的时候,在默认情况下CPU是随机切换线程。如果我们希望有规律低执行,就需要使用通信。
2、如何通信?
如果希望线程等待,就调用wait()方法;如果希望唤醒等待的线程,就调用notify()方法。这两个方法必须在同步代码块中执行,并且使用同步代码锁对象来调用。
Object类中的wait(): 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
notify() :唤醒在此对象监视器上等待的单个线程。
public class Demo01_DoubleThread {
/*
* 两个线程的通信
*/
public static void main(String[] args) {
final Print print = new Print();
new Thread() {
public void run() {
while (true) {
try {
print.print01();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
try {
print.print02();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Print {
private int flag = 1;
public void print01() throws InterruptedException {
synchronized (this) {
if (flag != 1) {
this.wait(); // 当前线程等待
}
System.out.print("坚");
System.out.print("持");
System.out.print("努");
System.out.print("力");
System.out.print("吧");
System.out.print("\r\n");
flag = 2;
this.notify(); // 随机唤醒单个等待的线程
}
}
public void print02() throws InterruptedException {
synchronized (this) {
if (flag != 2) {
this.wait();
}
System.out.print("J");
System.out.print("U");
System.out.print("S");
System.out.print("T");
System.out.print("\r\n");
flag = 1;
this.notify();
}
}
}
1.3 多个线程之间的通信(JDK1.5之前):
* notify()方法是随机唤醒一个线程
* notifyAll()方法是唤醒所有线程
* JDK5之前无法唤醒指定的一个线程
* 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, while来反复判断条件。
public class Demo02_MultiThread {
/*
* 三个线程的通信
* notifyAll():唤醒在此对象监视器上等待的所有线程。
*/
public static void main(String[] args) {
final Printer pt = new Printer();
new Thread() {
public void run() {
while (true) {
try {
pt.print01();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
try {
pt.print02();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
try {
pt.print03();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer {
private int flag = 1;
public void print01() throws InterruptedException {
synchronized (this) {
while (flag != 1) {
this.wait();
}
System.out.print("1....坚");
System.out.print("持");
System.out.print("努");
System.out.print("力");
System.out.print("吧");
System.out.print("\r\n");
flag = 2;
this.notifyAll();
}
}
public void print02() throws InterruptedException {
synchronized (this) {
while (flag != 2) {
this.wait();
}
System.out.print("2....J");
System.out.print("U");
System.out.print("S");
System.out.print("T");
System.out.print("\r\n");
flag = 3;
this.notifyAll();
}
}
public void print03() throws InterruptedException {
synchronized (this) {
while (flag != 3) {
this.wait();
}
System.out.print("3....P");
System.out.print("e");
System.out.print("a");
System.out.print("c");
System.out.print("e");
System.out.print("\r\n");
flag = 1;
this.notifyAll();
}
}
}
1.4 多个线程之间的通信(JDk1.5新特性):
1.同步
* 使用ReentrantLock类的lock()和unlock()方法进行同步
一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
public Condition newCondition():返回用来与此 Lock 实例一起使用的 Condition 实例。
Condition接口:
await():造成当前线程在接到信号或被中断之前一直处于等待状态。
signal():唤醒一个等待线程。
2.通信
* 使用ReentrantLock类的newCondition()方法可以获取Condition对象
* 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
* 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了
public class Demo02_MultiThread {
/*
* 三个线程的通信
* ReentrantLock/Condition,这是JDK1.5之后版本的新特新
*/
public static void main(String[] args) {
final Printer3 pt = new Printer3();
new Thread() {
public void run() {
while (true) {
try {
pt.print01();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
try {
pt.print02();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
while (true) {
try {
pt.print03();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer3 {
private ReentrantLock reentrantLock = new ReentrantLock(); // 创建一个互斥锁对象
private Condition condition1 = reentrantLock.newCondition(); // 获取的一个Condition对象
private Condition condition2 = reentrantLock.newCondition(); // 获取的一个Condition对象
private Condition condition3 = reentrantLock.newCondition(); // 获取的一个Condition对象
private int flag = 1;
public void print01() throws InterruptedException {
reentrantLock.lock(); // 获取锁
if (flag != 1) {
condition1.await(); // 等待
}
System.out.print("1....坚");
System.out.print("持");
System.out.print("努");
System.out.print("力");
System.out.print("吧");
System.out.print("\r\n");
flag = 2;
condition2.signal(); // 唤醒
reentrantLock.unlock(); // 释放锁
}
public void print02() throws InterruptedException {
reentrantLock.lock();
if (flag != 2) {
condition2.await();
}
System.out.print("2....J");
System.out.print("U");
System.out.print("S");
System.out.print("T");
System.out.print("\r\n");
flag = 3;
condition3.signal();
reentrantLock.unlock();
}
public void print03() throws InterruptedException {
reentrantLock.lock();
if (flag != 3) {
condition3.await();
}
System.out.print("3....P");
System.out.print("e");
System.out.print("a");
System.out.print("c");
System.out.print("e");
System.out.print("\r\n");
flag = 1;
condition1.signal();
reentrantLock.unlock();
}
}
二、多线程的高级应用:
2.1 线程组:
public class Demo04_ThreadGroup {
/**
* ThreadGroup线程组
*/
public static void main(String[] args) {
// demo01();
ThreadGroup group = new ThreadGroup("我是一个WeChat线程组");
MyRun mr = new MyRun();
Thread thread1 = new Thread(group, mr, "WeChat-张三");
Thread thread2 = new Thread(group, mr, "WeChat-李四");
group.setDaemon(true); // 把整个线程组设置为守护线程
System.out.println(thread1.getThreadGroup().getName()); // 获取线程thread1所在线程组的名字
System.out.println(thread2.getThreadGroup().getName()); // 获取线程thread2所在线程组的名字
}
private static void demo01() {
MyRun mr = new MyRun();
Thread thread1 = new Thread(mr, "WeChat-张三");
Thread thread2 = new Thread(mr, "WeChat-李四");
// 直接放在主线程中的线程,默认会分在main线程组中
ThreadGroup group1 = thread1.getThreadGroup();
ThreadGroup group2 = thread2.getThreadGroup();
System.out.println(group1.getName());
System.out.println(group2.getName());
}
}
class MyRun implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "---------"
+ i);
}
}
}
2.2 线程池:
程序启动一个新的线程成本比较高的,因为它涉及到与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是程序中需要创建大量的生存期短线程的时候,更应该考虑使用线程池。线程池中的线程在程序代码结束之后,并不会死亡,而是再次回到线程池,称为空闲状态,等待下一个对象来使用。JDK1.5版本起不需要手动写,由Java内置支持。
public class Demo05_Executor {
/**
* 线程池
*/
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2); // 创建线程池
// 将线程放入线程池,会启动线程池
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.shutdown(); // 关闭线程池
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "---------"
+ i);
}
}
}
2.3 多线程的实现方式(实现Callable接口):
public class Demo06_Callable {
/**
* Callable接口——线程的第三种实现方式
*/
public static void main(String[] args) throws InterruptedException,
ExecutionException {
ExecutorService pool = Executors.newFixedThreadPool(3); // 创建一个线程池
Future<Integer> future1 = pool.submit(new MyCallable(10)); // 将线程放进线程池中
Future<Integer> future2 = pool.submit(new MyCallable(40)); // 将线程放进线程池中
Future<Integer> future3 = pool.submit(new MyCallable(100)); // 将线程放进线程池中
System.out.println(future1.get());
System.out.println(future2.get());
System.out.println(future3.get());
pool.shutdown();
}
}
class MyCallable implements Callable<Integer> {
private int number;
public MyCallable() {
}
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception { // 求1到指定整数之间的全部整数之和
int sum = 0;
for (int i = 1; i <= number; i++) {
sum += i;
}
return sum;
}
}
三、常见设计模式:
3.1单例设计模式:
3.1.1 概述:
单例设计模式:保证类在内存中只有一个实例对象。比如BBS论坛中访客(当前在线人数)的实时统计,必须保证存储总数的对象只能是一个。
如何保证类在内存中只有一个对象?
控制类的创建,不让其他的类来创建本类对象——使用private修饰构造方法。
在本类中定义一个本类的对象。
对外提供一个公共的访问方法。
3.1.2 实现方式:
public class Demo01_Singleton {
/**
* 单例模式的两种实现:饿汉和懒汉
*/
public static void main(String[] args) {
Visitor visitor1 = Visitor.getInstance();
Visitor visitor2 = Visitor.getInstance();
System.out.println(visitor1 == visitor2);
}
}
/*
* 饿汉模式和懒汉模式的区别:
* 1、饿汉模式是以空间换时间,懒汉模式是以时间换空间
* 2、在多线程访问时,饿汉模式一定不会创建多个对象的;懒汉模式有可能会创建出多个对象
* 3、开发时一般是使用饿汉模式,面试笔试考懒汉
*/
class Visitor { // 懒汉模式
// 1.私有的构造方法,其他的类不能访问该构造方法
private Visitor() {
}
// 2.声明一个引用
private static Visitor visitor;
// 3.对外提供公共的访问方法
public static Visitor getInstance() { // 获取实例
if (visitor == null) {
visitor = new Visitor(); // 线程1等待,线程2会创建一个成功了
}
return visitor;
}
}
// class Visitor { // 饿汉模式
// // 1.私有的构造方法,其他的类不能访问该构造方法
// private Visitor() {
// }
//
// // 2.创建一个本类对象
// private static Visitor visitor = new Visitor();
//
// // 3.对外提供公共的访问方法
// public static Visitor getInstance() { // 获取实例
// return visitor;
// }
// }
3.1.3 Runtime类:
Ctrl+Shift+T:查看源码。
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时对象。应用程序不能创建自己的 Runtime 类实例。
public class Demo02_Runtime {
/**
* Runtime类的定义采用饿汉模式,与当前的运行环境相连接
*/
public static void main(String[] args) throws IOException {
Runtime time = Runtime.getRuntime(); // 获取运行时对象
time.exec("shutdown -s -t 300"); // 300s之后立即关机
time.exec("shutdown -a"); // 中止系统关机
}
}
3.1.4 定时器类:
public class Demo03_Timer {
/**
* 单例模式实现的定时器
*/
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer(); // 创建一个定时器对象
// 在指定时间安排指定任务
// Date的构造器中:
// year - 减 1900 的年份。
// month - 0-11 之间的月份。
// date - 一月中 1-31 之间的某一天。
// hrs - 0-23 之间的小时数。
// min - 0-59 之间的分钟数。
// sec - 0-59 之间的秒数。
// 第一参数是安排的任务,第二个参数是执行的时间,第三个参数是过多久之后再重复执行
timer.schedule(new MyTimerTask(), new Date(117, 6, 28, 16, 39, 10),
3000); // 2017-07-28-16:35:00
while (true) {
Thread.sleep(1000);
System.out.println("当前时间为:" + new Date());
}
}
}
class MyTimerTask extends TimerTask {
@Override
public void run() {
System.out.println("你应该被自己帅醒了!!!!");
}
}
3.2 简单工厂设计模式:
简单工厂设计模式,又被称为静态工厂方法模式,它定义一个具体的工厂类负责创建一些实例对象。
优点:客户端不需要负责对象的创建,从而明确了各个类的职责。
缺点:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要修改工厂类,不利于后期的维护。
3.3 工厂方法模式:
工厂方法模式中,抽象工厂类负责定义创建对象的接口,具体的对象创建工作由继承抽象工厂的具体类实现。
优点:客户端不需要负责对象的创建,从而明确了各个类的职责;如果有新的对象的增加,只需要增加一个具体的类和具体的工厂类即可,不影响原有的代码,后期维护容易,增强了系统的扩展性。
缺点:需要额外的编写代码,增加了工作量。