多线程
一个进程就是一个正在运行的程序。 线程就是进程中的执行单位。 一个进程里可以有多个线程故而称之为多线程(一个线程只属于一个进程)。 线程执行相当于去抢cpu执行的,当执行一个线程未执行完事可能下一个线程开始执行了。
线程创建
java中线程创建的方式有两种:一种是集成Thread类,如果类继承了Thread类那么这个类就被成为线程类,一种是实现接口
继承Thread类
继承Thread类必须实现run方法。 创建了几个线程类对象就相当于有几个线程 启用线程必须使用start方法
线程类的常用方法
start() 启用线程
setName() 线程命名
getName() 获取线程名称
复制代码
public class ThreadDemo extend Thread{
public void run(){
for(int i=0;i<100;i++){
System.out.println(getName()+i);
}
}
}
public class void test(){
public static void main(String[] aargs){
//创建线程类对象,创建几个线程类对象相当于几个线程
ThreadDemo threadDemo=new ThreadDemo();
threadDemo.setName("线程1");
threadDemo.setName("线程2");
ThreadDemo threadDemo2=new ThreadDemo();
//启用线程
threadDemo.start();
threadDemo2.start();
}
}
复制代码
实现Runnable接口
实现Runnable是实现的run 方法,使用runnable的好处就是能够操作多个线程之间数据的共享
public class RunnableDemo implements Runnable{
public void run(){
for(int i=0;i<100;i++){
System.out.peintln(Thread.currentThread().getName());
}
}
}
public class test{
public static void main(String[] args){
RunnableDemo runnableDemo=new RunnableDemo();
//开启线程需要转Threa对象
ThreaD thread=new Thread(runnableDemo);
thread.start();
}
}
复制代码
线程的执行状态
线程是有生命周期的 new Thread(创建线程类对象) 线程新生 start(启动线程) 线程准备期 cpu分配 运行状态 当cpu分配给哪个线程,线程就是运行状态
当线程未执行完,cpu被分配给其他线程,那么当前的线程进入准备状态,等待cpu分配
当线程执行完毕 线程进入死亡状态
当cpu分配给线程时,线程被其他因素终端,线程进入阻塞状态,当线程阻塞结束延时阻塞结束后进入准备状态等待继续执行。
当线程进入阻塞状态时,cpu则会被分配给其他线程
复制代码
多线程高并发和并行
高并发就是一段时间内cpu执行多个线程
并行就是同一时刻多个线程同时执行(并行只发生在多cpu的情况下)
线程调度
线程优先级
通过setPriority进行设置线程优先级,参数是0-10数字越大优先级越高,默认线程的优先级是5 ,设置优先级只是可以保证线程的邮优先级,并不能完全保证线程还是随机的。
join方法也可以进行优先级的提高。在线程开启之后调用该线程的join方法那么其他线程就需要等待当前开启join的线程先执行完
yield yield方法是礼让一般在run方法中调用就是先暂停当前线程的执行,礼让给其他线程
sleep 线程睡眠,参数是毫秒
setDaomon 守护线程,守护线程就是当其他线程执行完成之后,守护线程结束。
复制代码
public RunableDemo1 implments Runnable{
public void run(){
for(int i=0;i<100;i++){
System.out.println(1);
}
}
}
public class test {
public static void main(String[] args){
RunnableDemo1 demo1=new RunnableDemo1();
RunnableDemo1 demo2=new RunnableDemo1();
demo1.setPriotirty(10);
demo2.setPriotirty(1);
demo1.start();
demo2.start();
//demo1先运行的机率比demo2的概率大
}
}
复制代码
线程安全
线程锁
通过关键字可以保证代码执行完成之后才能被下一个进程执行
synchornized 关键字
这个关键字也可以写到方法上
//同步代码块,同步代码块需要添加锁,保证不同进程操作的锁是一样的
public class ThreadDemo implements Runnable{
private int num=100;
private Object obj=new Object();
@Override
public void run() {
while(true){
synchronized (obj){
if(num > 0){
System.out.println(Thread.currentThread().getName()+"卖了"+num+"票");
this.num--;
}else{
break;
}
}
}
}
}
//锁方法,锁方法的话那么当前的锁就是this
public class ThreadDemo implements Runnable{
private int num=100;
private Object obj=new Object();
@Override
public void run() {
while(true){
test();
}
}
public synchronized void test(){
if(num > 0){
System.out.println(Thread.currentThread().getName()+"卖了"+num+"票");
this.num--;
}
}
}
//静态方法锁,静态方法锁的锁是当前的类
public class ThreadDemo implements Runnable{
public static int num=100;
private Object obj=new Object();
@Override
public void run() {
while(true){
test();
}
}
public synchronized static void test(){
if(ThreadDemo.num > 0){
System.out.println(Thread.currentThread().getName()+"卖了"+num+"票");
num--;
}
}
}
复制代码
Lock锁
Lock就是一个同步锁的抽象类。 Lock的实现类
有两个方法 lock() 加锁 unlock() 结束锁
死锁
避免死锁出现的情况,就是避免使用同步锁嵌套
线程通信
线程通信是通过wait和notify方法,java的线程通信其实的本质就是通过同步锁操作同一个对象,然后就是wait等待和notify唤醒
//包子类
package com.Thread.Emaile;
public class Bzozi {
private String pi;
private String x;
private boolean bl=false;
public String getPi() {
return pi;
}
public void setPi(String pi) {
this.pi = pi;
}
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
public boolean isBl() {
return bl;
}
public void setBl(boolean bl) {
this.bl = bl;
}
}
//包子铺线程
public class Bozipu extends Thread{
public Bzozi bz;
public int count=0;
public Bozipu(Bzozi bz){
this.bz=bz;
}
@Override
public void run() {
while(true){
synchronized (bz){
test();
}
}
}
public void test(){
if(bz.isBl() == true){
try {
//如果有包子,那么包子铺线程等待,包子被吃
bz.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//没有包子开始做包子
if(count %3 ==1){
bz.setPi("杂粮皮");
bz.setX("猪肉大葱");
bz.setBl(true);
}else if(count%3 == 0){
bz.setPi("巧克力脆皮");
bz.setX("韭菜鸡蛋");
bz.setBl(true);
}else{
bz.setPi("抹茶");
bz.setX("奶油");
bz.setBl(true);
}
count++;
System.out.println("包子做好了,吃货可以来吃了");
bz.notify(); //唤醒吃货
}
}
//吃货线程
package com.Thread.Emaile;
public class Eat extends Thread{
public Bzozi bz;
public Eat(Bzozi bz){
this.bz=bz;
}
@Override
public void run() {
while(true){
synchronized (bz){
test();
}
}
}
public void test(){
//没有包子,等待包子铺做包子
if(bz.isBl() == false){
try {
bz.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("正在吃"+bz.getPi()+"皮"+bz.getX()+"馅的包子");
bz.setBl(false);
bz.notify();
}
}
//test
/**
* 吃包子的案例就是用bz来做锁
* 不管哪个线程先执行,如果是包子铺先执行,没有包子的话就做,做完之后唤醒吃货,有就等待吃货吃完之后再唤醒包子铺去做
* 如果是吃货先执行,那有就吃,没有就唤醒包子铺做
*/
public class test {
public static void main(String[] args) {
Bzozi bz=new Bzozi();
Thread eat=new Eat(bz);
Thread bzp=new Bozipu(bz);
eat.start();bzp.start();
}
}
复制代码
线程池
线程池的好处就是可以循环往复的利用线程。降低资源消耗,减少线程的创建和销毁
线程池使用
线程池的顶级接口 是Executor 但是并不是真正的线程池,只是一个执行现成的工具,线程池的实现接口 是ExecutorService。
具体实现类 ThreadPoolExecutor 使用可能的几个合并的线程执行每个提交的任务,通常使用Exceutor
public class test {
public static void main(String[] args) {
//利用静态方法获取实现类
ExecutorService executor= Executors.newFixedThreadPool(2);
//可以看到需要跑的任务有6个但是只有两个线程,所以两个线程交替执行
Thread thread=new Thread(new ThreadDemo());
executor.submit(thread);
executor.submit(thread);
executor.submit(thread);
executor.submit(thread);
executor.submit(thread);
executor.submit(thread);
//关闭线程池
executor.shutdown();
}
}
复制代码
Volatile关键字
首先需要了解可见性,可见性是一种复杂的属性。可见性,是指线程之间的可间性,就是一个线程修改的状态对于另外一个线程是可见的。所以需要用到volatile关键字来修饰变量,volatile关键字保证了变量的可见性。volatile关键字修饰的变量不允许线程内部缓存和重排序,即直接修改内存,所以对其他线程是可见的,但是volatile只能保证可见性,不能保证原子性,比如直接赋值这种就是保证了原子性使用了原子操作,但是使用a++就是一个非原子操作依然存在线程安全的问题。
原子性:原子性是世界上最小的单位,具有不可分割性,比如a=10是不可分割的是一个原子操作,1=a+1是可以分割的所以不是一个原子操作,非原子操作都会存在线程安全问题,所以需要使用同步锁实现原子操作
volatile的原理 volatile是java提供的一种稍弱的同步机制,即vloatile变量,用来保证变量的更新操作可以通知到其他线程,当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量的操作与与其他内存一起重排序。
在访问volatile变量时不会执行枷锁操作,因此也就不会使执行线程阻塞,因此volatile是一种比sychronized更轻量级的同步机制
当变量被volatile关键字修饰时,jvm保证了每次读变量都存内存中读取,跳过cpu缓存这一步
当一个变量被定义为volatile之后,将具有两种特性
对所有线程的可见性以及禁止指令重排序优化
复制代码
volatile的变量的读性能与普通变量几乎相同,但是写的操作稍慢,因为需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序行为。
设计模式
设计模式分类
设计模式分为三类: 创建型模式 结构型模式 行为型模式
创建型模式
创建型模式就是将对象的创建与使用分离
单例,原型,工厂方法,抽象工厂,建造者模式
复制代码
结构型模式
结构型模式就是将类或者对象按照某种布局组成更大的结构
代理,适配器,桥接,装饰,外观,享元,组合
复制代码
行为型模式
行为型模式就是用于描述,类或者对象之间怎样互相协作共同完成单个对象无法单独完成的任务
模板方法
策略
命令
职责链
状态(状态机模式可以参考promise)
观察者(vue里的数据响应就是使用了观察者模式)
中介者
迭代器
访问者
备忘录模式
复制代码
单例模式
单例模式是最简单的设计模式之一,属于创建型模式,这种模式涉及到一个单一的类,该类负责创建自己的对象。同时确保只有一个对象被创建,提供访问其唯一对象的方法,可以直接访问,不被实例化。
单例模式步骤
私有化构造方法
在当前类中提供一个输出该类对象的方法
复制代码
懒汉单例模式(线程不安全) 第一次获取对象的时候才会进行赋值,节省资源
解决懒汉模式的线程安全问题就是加锁,既保证安全又需要保证效率就使用锁+线程
饿汉模式-单例模式(急加载) 该类第一次被访问时立即创建对象 。不存在线程安全问题
缺点: 资源占用大,资源浪费
原型(Prootype)模式
原型模式就是将一个对象作为原型,通过对其进行赋值而克隆出多个和原型类似的新实例
工厂模式
统一管理某个类的创建
简单工厂类 使用静态方法
public class Facetory implements Animals{
// 静态工厂模式
private static Map<String,Thacher> storeMap;
public static void init(){
storeMap=new HashMap<String,Thacher>();
storeMap.put("t1",new Thacher("张三","男",20));
storeMap.put("t2",new Thacher("李四","男",21));
storeMap.put("t3",new Thacher("王五","男",22));
}
public static Thacher getTeacher(String key){
return storeMap.get(key);
}
}
复制代码
实例工厂方法 不加静态
public class Facetory implements Animals{
private Map<String,Thacher> storeMap;
public Facetory(){
storeMap=new HashMap<String,Thacher>();
}
public Thacher getTeacher(String key){
return storeMap.get(key);
}
}
//test
Facetory facetory=new Facetory();
Thacher t1=facetory.getTeacher("t1");
System.out.println(t1.getName());
复制代码
模板模式
模板模式就是在类里写一个抽象方法,抽象方法的实现在使用的时候写
//ModelDemo
public abstract class ModelDemo {
public double js(double a,double b){
double dl=ys(a,b);
System.out.println("运算结果是"+dl);
return dl;
}
public abstract double ys(double a,double b);
}
//test
public class test {
public static void main(String[] args) {
double dl=new ModelDemo(){
@Override
public double ys(double a, double b) {
return a*b;
}
}.js(18,21);
}
}
复制代码
装饰模式
对原有功能的增强,装饰模式就是,在装饰类里,调用原有的功能并且执行新的功能
// zq
public class zQ {
private Object obj;//要增强的对象
public zQ() {
}
public zQ(Object obj){
this.obj=obj;
}
public void zq(){
System.out.println("看视频");
System.out.println("看电子书");
if(obj instanceof Phone){
((Phone) this.obj).call();
}
}
}
//test
public class test {
public static void main(String[] args) {
zQ zq=new zQ(new Phone());
zq.zq();
}
}
复制代码
面向对象思想设计原则
单一职业原则 高内聚,低耦合,一个类只有一个功能,
开闭原则,对扩展进行开放,对修改进行关闭
里氏替换原则,同一个继承体系中的对象应该有共同的行为特征
依赖注入原则
依赖抽象类,不要依赖于具体实现
接口分离原则
一个接口只提供一种对外的功能
迪米特原则
降低对象之间的耦合,提高可维护性
作者:青山黛玛
链接:https://juejin.cn/post/7205144461923549240