目录
Timer计时器
public class Demo5_Timer {
/**
* @param args
* 计时器
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Timer t = new Timer();
//这里的3000表示的是每三秒重复执行一次
t.schedule(new MyTimerTask(), new Date(114,9,15,10,54,20),3000);
while(true) {
System.out.println(new Date());
Thread.sleep(1000);
}
}
}
class MyTimerTask extends TimerTask {
@Override
public void run() {
System.out.println("起床背英语单词");
}
}
1、多线程通信
(1)两个线程间的通信
* 1.什么时候需要通信
* 多个线程并发执行时, 在默认情况下CPU是随机切换线程的
* 如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
* 2.怎么通信
* 如果希望线程等待, 就调用wait()
* 如果希望唤醒等待的线程, 就调用notify();
* 这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
(2)多个线程通信的问题(三个或三个以上间的线程通信)
* notify()方法是随机唤醒一个线程
* notifyAll()方法是唤醒所有线程
* JDK5之前无法唤醒指定的一个线程
* 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件
/**
* Created by asus on 2019/11/13.
*/
public class xainchengwait {
private static int a = 1;
public static void main(String[] args) {
print p = new print();
Thread t1 = new Thread()
{
public void run()
{
while(true)
{
p.print1();
}
}
};
Thread t2 = new Thread()
{
public void run()
{
while(true)
{
p.print2();
}
}
};
t1.start();
t2.start();
}
}
class print{
public static int a = 1;
public void print1()
{
synchronized (this)
{
if(a!=1)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("黑");
System.out.print("马");
System.out.print("程");
System.out.print("序");
System.out.println();
a=2;
this.notify();//随机唤醒单个线程
}
}
public void print2()
{
synchronized (this)
{
if(a!=2)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("传");
System.out.print("智");
System.out.print("博");
System.out.print("客");
System.out.println();
a=1;
this.notify();
}
}
}
2、JDK1.5的新特性互斥锁
* 1.同步(可以用来代替synchronize)
* 使用ReentrantLock类的lock()和unlock()方法进行同步
* 2.通信
* 使用ReentrantLock类的newCondition()方法可以获取Condition对象
* 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
* 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了
3、线程组的概述和使用
A:线程组概述:
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于主线程组。
public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组
public final String getName()//通过线程组对象获取他组的名字
我们也可以给线程设置分组
* 1,ThreadGroup(String name) 创建线程组对象并给其赋值名字
* 2,创建线程对象
* 3,Thread(ThreadGroup?group, Runnable?target, String?name)
* 4,设置整组的优先级或者守护线程
案例演示
线程组的使用,默认是主线程组
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr, "张三");
Thread t2 = new Thread(mr, "李四");
//获取线程组
// 线程类里面的方法:public final ThreadGroup getThreadGroup()
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
// 线程组里面的方法:public final String getName()
String name1 = tg1.getName();
String name2 = tg2.getName();
System.out.println(name1);
System.out.println(name2);
// 通过结果我们知道了:线程默认情况下属于main线程组
// 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组
System.out.println(Thread.currentThread().getThreadGroup().getName());
* 自己设定线程组
*
// ThreadGroup(String name)
ThreadGroup tg = new ThreadGroup("这是一个新的组");
MyRunnable mr = new MyRunnable();
// Thread(ThreadGroup group, Runnable target, String name)
Thread t1 = new Thread(tg, mr, "张三");
Thread t2 = new Thread(tg, mr, "李四");
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
//通过组名称设置后台线程,表示该组的线程都是后台线程
tg.setDaemon(true);
4、线程的五种状态
新建、就绪、运行、阻塞、死亡
新建(new):新创建了一个线程对象。
(也就是刚new出来的线程对象)
就绪(可运行状态)(runnable):线程对象创建后,当调用线程对象的 start()方法,该线程处于就绪状态,等待被线程调度选中,获取cpu的使用权。
(就绪状态就是调用了start,等待分配时间片的过程,这个等待分配时间片的过程就是当cpu切换到该线程的时候)
运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。
注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
(分配到了时间片,并进行工作的过程)
阻塞(block):处于运行状态中的线程由于某种原因,暂时放弃对 CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被 CPU 调用以进入到运行状态。
阻塞的情况分三种:
(一). 等待阻塞:运行状态中的线程执行 wait()方法,JVM会把该线程放入等待队列(waitting queue)中,使本线程进入到等待阻塞状态;
(二). 同步阻塞:线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),,则JVM会把该线程放入锁池(lock pool)中,线程会进入同步阻塞状态;
(三). 其他阻塞: 通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
死亡(dead)(结束):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
线程阻塞通常是指一个线程在执行过程中暂停,以等待某个条件的触发。
5、线程池的概述和使用
线程池概述:
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
内置线程池的使用概述:
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads) -------参数是线程的数目
public static ExecutorService newSingleThreadExecutor()-----这里的话不需要参数,因为只能放一条线程
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
* Future<?> submit(Runnable task)
* <T> Future<T> submit(Callable<T> task)
使用步骤:
创建线程池对象
创建Runnable实例
提交Runnable实例
关闭线程池
案例演示
提交的是Runnable
// public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
//这了的MyRunnable 是自己书写的线程类,实现了runnable接口
pool.submit(new MyRunnable());//将线程放到线程池里面并执行
pool.submit(new MyRunnable());
//结束线程池,假如不结束线程池的话,线程是不会停止的,会一直运行下去
pool.shutdown();
多线程程序实现的方式3
* 提交的是Callable*
// 创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));
// V get()
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
// 结束
pool.shutdown();
public class MyCallable implements Callable<Integer> {
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
}
多线程程序实现的方式3的好处和弊端
好处:
可以有返回值
可以抛出异常
弊端:
代码比较复杂,所以一般不用
6、设计模式的oop七大原则
(1)开闭原则:对扩展开放,对修改关闭。一个对象(模板,类,方法)只要是在生命周期内,都会发生变化,而开闭原则就是在不修改原有模块的基础上,扩展其功能
(2)里氏替换原则:继承必须确保超类所拥有的性质在子类中依然成立。父类能出现的地方,可以用子类代替,子类拥有父类的所有属性和方法,通俗来说子类拥有父类的功能,可以拓展父类的功能,但是不能修改父类的功能
(3)依赖倒置原则:要面向接口编程,不要面向实现编程。程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
左图就是在说 高层依赖低层,如果这个高层抹口红就只能抹口红,不能在抹其他的 ,要是想在抹别的,就要再加方法,太啰嗦
而右图就是在说明定义了一个抽象类抹,如果没有口红了还可以抹眼影啊,粉底之类的,人与眼影之间依赖抹这个抽象类来实现,这个抹眼影 这个细节依赖于抽象抹这个类,而这个高层人也可以创建不同的高层
(4)单一职责原则:控制类的粒度大小,将对象解耦、提高其内聚性。一个类只负责一个领域的内容,简而言之就是自己的类负责自己的事情,与别的类互不干涉
(5)接口隔离原则:要为各个类建立他们需要的专用接口。客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
(6)迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话
是一个类应该对其他的类有最少的了解,因为当一个类发生改变的时候,另一个以来的类也会发生相应的改变,两个类之间依赖程度越大,耦合性越高,改变的类越多,相依赖的类影响更大,为了解决这种情况,提出了迪米特法则,减少相应类的依赖
(7)合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
组合/聚合复用原则经常又叫做合成复用原则。该原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分:新的对象通过向这些对象的委派达到复用已有功能的目的。这里提示!!!要尽量的使用组合聚和原则,尽量不要使用继承,因为在子类继承父类的时候,父类把细节全部暴露给了子类,并且在父类发生改变的时候,子类也会相应发生改变,不利于类的扩展,耦合性比较高,而组合聚和原则他们的新对象与已有对象的emmmmm‘沟通’都是通过抽象类进行呢,能够避免继承的缺点,并且符合单一原则
什么是合成?合成表示一种强的拥有关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样,打个比方:人有两个胳膊,胳膊和人就是部分和整体的关系,人去世了,那么胳膊也就没用了,也就是说胳膊和人的生命周期是相同的
什么是聚合?聚合表示一种弱的拥有关系,体现的是A对象可以包含B对象,但是B对象并不是A对象的一部分,打个比方:人是群居动物,所以每个人属于一个人群,一个人群可以有多个人,所以人群和人是聚合的关系
7、单例设计模式
单例设计模式:保证类在内存中只有一个对象。
如何保证类在内存中只有一个对象呢?
(1)控制类的创建,不让其他类来创建本类的对象。private
(2)在本类中定义一个本类的对象。Singleton s;
(3)提供公共的访问方式。 public static Singleton getInstance(){return s}
单例写法两种:
(1)饿汉式 (开发用这种方式)。
//饿汉式
class Singleton {
//1,私有构造函数
private Singleton(){}
//2,创建本类对象
private static Singleton s = new Singleton();
//3,对外提供公共的访问方法
public static Singleton getInstance() {
return s;
}
public static void print() {
System.out.println("11111111111");
}
}
(2)懒汉式
//懒汉式,单例的延迟加载模式
class Singleton {
//1,私有构造函数
private Singleton(){}
//2,声明一个本类的引用
private static Singleton s;
//3,对外提供公共的访问方法
public static Singleton getInstance() {
if(s == null)
//线程1,线程2
s = new Singleton();
return s;
}
public static void print() {
System.out.println("11111111111");
}
}
8、工厂模式
(简单工厂模式【静态工厂模式】、工厂模式、抽象工厂模式)
核心:
(1)实例化对象不使用new,用工厂方法代替
(2)将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类解耦
三种模式
(1)简单工厂模式
用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有的代码)
(2)工厂模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
(3)抽象工厂模式
围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂
(1)简单工厂模式
* A:简单工厂模式概述
* 又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
* B:优点
* 客户端不需要在负责对象的创建,从而明确了各个类的职责
* C:缺点
* 这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护
Demo(生产车子)
(1)生成车子的接口
//生产车 public interface Car { void name(); }
(2)宝马类和特斯拉类
public class BWM implements Car { @Override public void name() { System.out.println("宝马"); } }
public class TeSiLa implements Car { @Override public void name() { System.out.println("特斯拉"); }
(3)工厂
public class CarFactory { static Car getCar(String car){ if("BWM".equals(car)){ return new BWM(); }else if("TeSiLa".equals(car)){ return new TeSiLa(); } return null; } }
(4)消费者
//消费者 public class Consumer { public static void main(String[] args) { // /* // 传统的模式需要自己去new,这样就调用者与我们的代码实现耦合太高,所以需要使用工厂模式 // */ // BWM bwm = new BWM(); // TeSiLa teSiLa = new TeSiLa(); // bwm.name(); // teSiLa.name(); // Car car1 = (Car) CarFactory.getCar("BWM"); Car car2 = (Car) CarFactory.getCar("TeSiLa"); car1.name(); car2.name(); //如果使用这种方法有一种坏处,例如我们需要增加一种汽车奔驰,需要需要修改我们工厂的源代码 } }
(2)工厂方法模式
* A:工厂方法模式概述
* 工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
* B:优点
* 客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
* C:缺点
* 需要额外的编写代码,增加了工作量
Demo
(1)生成车子的接口
//生产车 public interface Car { void name(); }
(2)宝马类和特斯拉类
public class BWM implements Car { @Override public void name() { System.out.println("宝马"); } }
public class TeSiLa implements Car { @Override public void name() { System.out.println("特斯拉"); }
(3)工厂接口
public interface CarFactory { Car getCar(); }
(4)特斯拉工厂和宝马工厂
public class BMWFactory implements CarFactory{ @Override public Car getCar() { return new BWM(); } }
public class TESILAFactory implements CarFactory { @Override public Car getCar() { return new TESILA(); } }
(5)消费者
//消费者 public class Consumer { public static void main(String[] args) { Car car1 = new BMWFactory().getCar(); Car car2 = new TESILAFactory().getCar(); car1.name(); car2.name(); } }
(3)抽象工厂模式
定义: 抽象工厂模式提供了一个创建一系列相关或者相互依赖的对象的接口,无需指定他们具体的类
使用场景:
(1)客户端不依赖产品类实例如何被创建,实现等细节
(2)强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
(3)提供一个产品类的库,所有产品以相同的接口出现,从而使得客户端不依赖于具体的实现
优点:
(1)具体产品在应用层的代码提高,无需关心创建细节
(2)将一个系列的产品统一到一起创建
缺点:
(1)规定了所有可能被创建的产品集合,产品族中扩展的产品困难
(2)增加了系统的抽象性和理解难度
Demo(华为和小米的手机和路由为例)
(1)手机接口与路由接口
public interface IPhoneProduct { void start();//开机 void shutdown();//关机 void sendSms();//发送消息 void lookVideo();//看视频 }
public interface IRouterProduct { void start();//开机 void shutdown();//关机 void set();//设置路由配置 void updateIp();//更改路由ip }
(2)华为手机、小米手机、华为路由、小米路由
public class HuaWeiPhone implements IPhoneProduct { @Override public void start() { System.out.println("华为手机开机"); } @Override public void shutdown() { System.out.println("华为手机关机"); } @Override public void sendSms() { System.out.println("华为手机发送信息"); } @Override public void lookVideo() { System.out.println("华为手机看视频"); } }
public class HuaWeiRouter implements IRouterProduct { @Override public void start() { System.out.println("华为路由开机"); } @Override public void shutdown() { System.out.println("华为路由关机"); } @Override public void set() { System.out.println("设置华为路由配置"); } @Override public void updateIp() { System.out.println("更改华为路由的ip"); } }
public class XiaoMiPhone implements IPhoneProduct{ @Override public void start() { System.out.println("小米手机开机"); } @Override public void shutdown() { System.out.println("小米手机关机"); } @Override public void sendSms() { System.out.println("小米手机发送信息"); } @Override public void lookVideo() { System.out.println("小米手机看视频"); } }
public class XiaoMiRouter implements IRouterProduct { @Override public void start() { System.out.println("小米路由开机"); } @Override public void shutdown() { System.out.println("小米路由关机"); } @Override public void set() { System.out.println("设置小米路由的配置"); } @Override public void updateIp() { System.out.println("更新小米路由的ip"); } }
(3)抽象工厂接口
public interface AbstractFactory { //生产手机 IPhoneProduct getIPhoneProduct(); //生产路由 IRouterProduct getIRouterProduct(); }
(4)小米工厂和华为工厂
//华为工厂 public class HuaWeiFactory implements AbstractFactory { @Override public IPhoneProduct getIPhoneProduct() { return new HuaWeiPhone(); } @Override public IRouterProduct getIRouterProduct() { return new HuaWeiRouter(); } }
//小米工厂 public class XiaoMiFactory implements AbstractFactory { @Override public IPhoneProduct getIPhoneProduct() { return new XiaoMiPhone(); } @Override public IRouterProduct getIRouterProduct() { return new XiaoMiRouter(); } }
(5)消费者
public class Client { public static void main(String[] args) { //华为工厂 HuaWeiFactory huaWeiFactory = new HuaWeiFactory(); //生产华为手机 IPhoneProduct huaWeiPhone = huaWeiFactory.getIPhoneProduct(); //生产华为路由 IRouterProduct huaWeiRouter = huaWeiFactory.getIRouterProduct(); huaWeiPhone.sendSms(); huaWeiRouter.updateIp(); //小米工厂 XiaoMiFactory xiaoMiFactory = new XiaoMiFactory(); //生产小米手机 IPhoneProduct xiaoMiPhone = xiaoMiFactory.getIPhoneProduct(); //生产小米路由 IRouterProduct xiaoMiRouter = xiaoMiFactory.getIRouterProduct(); xiaoMiPhone.sendSms(); xiaoMiRouter.updateIp(); } }
工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例,也就是创建的是一个产品线下的多个产品。
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
工厂方法创建 "一种" 产品,他的着重点在于"怎么创建",也就是说如果你开发,你的大量代码很可能围绕着这种产品的构造,初始化这些细节上面。也因为如此,类似的产品之间有很多可以复用的特征,所以会和模版方法相随。
抽象工厂需要创建一些列产品,着重点在于"创建哪些"产品上,也就是说,如果你开发,你的主要任务是划分不同差异的产品线,并且尽量保持每条产品线接口一致,从而可以从同一个抽象工厂继承。