Java多线程编程核心技术---线程间通信(二)

通过管道进行线程间通信:字节流

Java提供了各种各样的输入/输出流Stream可以很方便地对数据进行操作,其中管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据,一个线程发送数据到输出管道,另一个线程从输入管道中读数据。通过使用管道,实现不同线程间的通信,无需借助于类似临时文件之类的东西。

JDK提供了4个类来使线程间可以进行通信:

  1. PipedInputStream和PipedOutputStream
  2. PipedReader和PipedWriter
public class WriteData {
    public void writeMethod(PipedOutputStream out) {
        try {
            System.out.println("write:");
            for (int i = 0; i < 100; i++) {
                String outData = "" + (i + 1);
                out.write(outData.getBytes());
                //System.out.print(outData);
            }
            //System.out.println();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ReadData {
    public void readMethod(PipedInputStream input) {
        try {
            System.out.println("read:");
            byte[] byteArray = new byte[20];
            int readLength = input.read(byteArray);
            while (readLength != -1) {
                String newData = new String(byteArray, 0, readLength);
                System.out.println(newData);
                readLength = input.read(byteArray);
            }
            System.out.println();
            input.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ThreadWrite extends Thread {
    private WriteData writeData;
    private PipedOutputStream out;
    
    public ThreadWrite(WriteData writeData, PipedOutputStream out) {
        super();
        this.out = out;
        this.writeData = writeData;
    }
    
    @Override
    public void run() {
        writeData.writeMethod(out);
    }
}

public class ThreadRead extends Thread {
    private ReadData readData;
    private PipedInputStream input;
    
    public ThreadRead(ReadData readData, PipedInputStream input) {
        super();
        this.input = input;
        this.readData = readData;
    }
    
    @Override
    public void run() {
        readData.readMethod(input);
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
            
            PipedInputStream inputStream = new PipedInputStream();
            PipedOutputStream outputStream = new PipedOutputStream();
            
            outputStream.connect(inputStream);
            //inputStream.connect(outputStream);
            
            ThreadRead threadRead = new ThreadRead(readData, inputStream);
            ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
            
            threadRead.start();//先启动读线程
            
            Thread.sleep(1000);
            
            threadWrite.start();//后启动写线程
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

read:
write:
123456
78910111213141516171
81920212223242526272
8293031323334353637
383940414243
44454647484950
51525354555657
58596061626364
65666768697071
72737475767778
798081828384
858687888990
91929394959697
9899100

先启动读线程,由于没有数据被写入,读线程阻塞在readLength = input.read(byteArray),直到有数据被写入才继续往下运行。

通过管道进行线程间通信:字符流
public class WriteData {
    public void writeMethod(PipedWriter writer) {
        try {
            System.out.println("write:");
            for (int i = 0; i < 100; i++) {
                String outData = "" + (i + 1);
                writer.write(outData);
                //System.out.print(outData);
            }
            //System.out.println();
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ReadData {
    public void readMethod(PipedReader reader) {
        try {
            System.out.println("read:");
            char[] array = new char[20];
            int readLength = reader.read(array);
            while (readLength != -1) {
                String newData = new String(array, 0, readLength);
                System.out.println(newData);
                readLength = reader.read(array);
            }
            System.out.println();
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ThreadWrite extends Thread {
    private WriteData writeData;
    private PipedWriter writer;
    
    public ThreadWrite(WriteData writeData, PipedWriter writer) {
        super();
        this.writer = writer;
        this.writeData = writeData;
    }
    
    @Override
    public void run() {
        writeData.writeMethod(writer);
    }
}

public class ThreadRead extends Thread {
    private ReadData readData;
    private PipedReader reader;
    
    public ThreadRead(ReadData readData, PipedReader reader) {
        super();
        this.reader = reader;
        this.readData = readData;
    }
    
    @Override
    public void run() {
        readData.readMethod(reader);
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
            
            PipedReader reader = new PipedReader();
            PipedWriter writer = new PipedWriter();
            
//          writer.connect(reader);
            reader.connect(writer);
            
            ThreadRead threadRead = new ThreadRead(readData, reader);
            ThreadWrite threadWrite = new ThreadWrite(writeData, writer);
            
            threadRead.start();
            
            Thread.sleep(1000);
            
            threadWrite.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

read:
write:
12345678910111213141
51617181920212223242
52627282930313233343
53637383940414243444
54647484950515253545
55657585960616263646
56667686970717273747
57677787980818283848
58687888990919293949
596979899100

打印结果和前一个例子基本一样。此实验是在两个线程中通过管道流进行字符数据的传输。


等待/通知:交叉备份

创建20个线程,10个线程将数据备份到A数据库中,10个线程将数据备份到B数据库中,并且备份A数据库和备份B数据库是交叉进行的。

public class DBTools {
    volatile private boolean prevIsA = false;
    synchronized public void backupA(){
        try {
            while (prevIsA) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("☆☆☆☆☆");
            }
            prevIsA = true;
            notifyAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    synchronized public void backupB(){
        try {
            while (!prevIsA) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("★★★★★");
            }
            prevIsA = false;
            notifyAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private DBTools dbTools;
    public ThreadA(DBTools dbTools) {
        super();
        this.dbTools = dbTools;
    }
    
    @Override
    public void run() {
        dbTools.backupA();
    }
}

public class ThreadB extends Thread {
    private DBTools dbTools;
    public ThreadB(DBTools dbTools) {
        super();
        this.dbTools = dbTools;
    }
    
    @Override
    public void run() {
        dbTools.backupB();
    }
}

public class Main {
    public static void main(String[] args) {
        DBTools dbTools = new DBTools();
        for (int i = 0; i < 20; i++) {
            ThreadA threadA = new ThreadA(dbTools);
            threadA.start();
            ThreadB threadB = new ThreadB(dbTools);
            threadB.start();
        }
    }
}

控制台打印结果如下:

......
★★★★★
★★★★★
★★★★★
★★★★★
★★★★★
☆☆☆☆☆
☆☆☆☆☆
☆☆☆☆☆
☆☆☆☆☆
☆☆☆☆☆
★★★★★
★★★★★
★★★★★
★★★★★
★★★★★
☆☆☆☆☆
☆☆☆☆☆
☆☆☆☆☆
☆☆☆☆☆
☆☆☆☆☆
......

交替打印是使用prevIsA作为标记来实现的。


方法join的使用

在很多情况下,主线程创建并启动子线程,如果子线程要进行大量运算,主线程往往比子线程先运行完毕。如果主线程想要等待子线程执行完之后再结束,就需要用到join方法。

先看一个实验。

public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            int secondValue = (int)(Math.random() * 10000);
            System.out.println(secondValue);
            Thread.sleep(secondValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
//      Thread.sleep(?);
        System.out.println("我想在thread对象执行完毕之后再执行");
        System.out.println("但是上面sleep()中的值写多少呢?");
        System.out.println("答案是:不能确定!!!");
    }
}
用join()方法来解决上面的问题
public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            int secondValue = (int)(Math.random() * 10000);
            System.out.println(secondValue);
            Thread.sleep(secondValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        thread.start();
//      Thread.sleep(?);
        thread.join();
        System.out.println("我想在thread对象执行完毕之后再执行");
    }
}

控制台打印结果如下:

9230
我想在thread对象执行完毕之后再执行

方法join()的作用是使所属线程对象x正常执行run()方法中的任务,而使当前线程进行无限期的阻塞。等线程x执行完毕之后再执行当前线程后续的代码。


方法join()与异常

在join()过程中,如果当前线程对象被中断,则当前线程出现异常。

public class ThreadA extends Thread {
    
    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            String newString = new String();
            Math.random();
        }
    }
}

public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            ThreadA a = new ThreadA();
            a.start();
            a.join();
            System.out.println("线程B在run end处打印");
        } catch (Exception e) {
            System.out.println("线程B在catch处打印");
            e.printStackTrace();
        }
    }
}

public class ThreadC extends Thread {
    private ThreadB threadB;
    public ThreadC(ThreadB threadB) {
        super();
        this.threadB = threadB;
    }
    
    @Override
    public void run() {
        threadB.interrupt();
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            ThreadB b = new ThreadB();
            b.start();
            Thread.sleep(2000);
            ThreadC c = new ThreadC(b);
            c.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

线程B在catch处打印
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.Thread.join(Unknown Source)
    at java.lang.Thread.join(Unknown Source)
    at com.umgsai.thread.thread38.ThreadB.run(ThreadB.java:9)

此时程序不结束,a线程扔在运行。


方法join(long)的使用
public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("MyThread开始,time = " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("MyThread结束,time = " + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        try {
            MyThread thread = new MyThread();
            thread.start();
            thread.join(2000);
            System.out.println("main线程结束 time = " + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

MyThread开始,time = 1466909154102
main线程结束 time = 1466909156103
MyThread结束,time = 1466909159102

如果将main中的join(2000)改成Thread.sleep(2000),运行效果是一样的,main线程都是等待2秒。主要区别来自于这两个方法对同步的处理上。


方法join(long)与sleep(long)的区别

方法join(long)的功能在内部是使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。

方法join(long)源代码如下:

public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);//释放锁
            now = System.currentTimeMillis() - base;
        }
    }
}

当执行wait(long)方法后,当前线程的锁被释放,其他线程就可以调用此线程中的同步方法了。

public class ThreadA extends Thread { 
    private ThreadB b;
    public ThreadA(ThreadB b) {
        super();
        this.b = b;
    }
    @Override
    public void run() {
        try {
            synchronized (b) {
                b.start();
                Thread.sleep(6000);不释放b对象锁
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ThreadB extends Thread { 
    @Override
    public void run() {
        try {
            System.out.println("B begin time = " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("B end time = " + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    synchronized public void bService(){
        System.out.println("bService time = " + System.currentTimeMillis());
    }
}

public class ThreadC extends Thread {
    private ThreadB threadB;
    public ThreadC(ThreadB threadB) {
        super();
        this.threadB = threadB;
    }
    
    @Override
    public void run() {
        threadB.bService();
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            ThreadB b = new ThreadB();
            ThreadA a = new ThreadA(b);
            a.start();
            Thread.sleep(1000);
            ThreadC c = new ThreadC(b);
            c.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

B begin time = 1466927915044
B end time = 1466927920045
bService time = 1466927921044

将线程A做如下修改:

public class ThreadA extends Thread { 
    private ThreadB b;
    public ThreadA(ThreadB b) {
        super();
        this.b = b;
    }
    @Override
    public void run() {
        try {
            synchronized (b) {
                b.start();
                b.join();//释放b对象锁
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此时控制台打印结果如下:

B begin time = 1466927989797
bService time = 1466927990801
B end time = 1466927994798

方法join()后面的代码提前运行:出现意外
public class ThreadA extends Thread {
    private ThreadB b;
    public ThreadA(ThreadB b) {
        super();
        this.b = b;
    }
    @Override
    public void run() {
        try {
            synchronized (b) {
                System.out.println(Thread.currentThread().getName() + " A begin time = " + System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println(Thread.currentThread().getName() + " A end time = " + System.currentTimeMillis());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ThreadB extends Thread {
    @Override
    synchronized public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " B begin time = " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName() + " B end time = " + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            ThreadB b = new ThreadB();
            ThreadA a = new ThreadA(b);
            a.start();
            b.start();
            b.join(2000);
            System.out.println("main end " + System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

P187 此节未完待续...

类ThreadLocal的使用

变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static变量。类ThreadLocal为每个线程绑定自己的值。

每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保存到其中。ThreadLocalMap针对每个thread保留一个entry,如果对应的thread不存在则会调用initValue。

方法get()与null
public class Run {
    public static ThreadLocal t1 = new ThreadLocal<>();
    public static void main(String[] args) {
        if (t1.get() == null) {
            System.out.println("从未放过值");
            t1.set("我的值");
        }
        System.out.println(t1.get());
        System.out.println(t1.get());
    }
}

控制台打印结果如下:

从未放过值
我的值
我的值

类ThreadLocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值是可以放入ThreadLocal类中进行保存的。


验证线程变量的隔离性
public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.t1.set("ThreadA " + (i + 1));
                System.out.println("ThreadA getValue=" + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.t1.set("ThreadB " + (i + 1));
                System.out.println("ThreadB getValue=" + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            ThreadA a = new ThreadA();
            ThreadB b = new ThreadB();
            a.start();
            b.start();
            for (int i = 0; i < 100; i++) {
                Tools.t1.set("Thread main " + (i + 1));
                System.out.println("Thread main getValue=" + Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

......
Thread main getValue=Thread main 99
ThreadB getValue=ThreadB 99
ThreadA getValue=ThreadA 99
Thread main getValue=Thread main 100
ThreadA getValue=ThreadA 100
ThreadB getValue=ThreadB 100

虽然三个线程都向t1对象中set()数据值,但每个线程还是能取出自己的数据。

第一次调用ThreadLocal类的get()方法返回值是null。可以设置默认值。

public class ThreadLocalExt extends ThreadLocal {
    public static ThreadLocalExt t1 = new ThreadLocalExt();
    
    @Override
    protected Object initialValue() {
        return "This is the default value";
    }
    
    public static void main(String[] args) {
        if (t1.get() == null) {
            System.out.println("从未放过值");
            t1.set("我的值");
        }
        System.out.println(t1.get());
        System.out.println(t1.get());
    }
}

控制台打印结果如下:

This is the default value
This is the default value

子线程和父线程各自拥有自己的值

public class ThreadLocalExt extends ThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }
}

public class Tools {
    public static ThreadLocalExt t1 = new ThreadLocalExt();
}

public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println("ThreadA取值:" + Tools.t1.get());
                Thread.sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println("Main取值:" + Tools.t1.get());
                Thread.sleep(100);
            }
            Thread.sleep(5000);
            ThreadA a = new ThreadA();
            a.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

Main取值:1467118837063
Main取值:1467118837063
Main取值:1467118837063
Main取值:1467118837063
Main取值:1467118837063
ThreadA取值:1467118842566
ThreadA取值:1467118842566
ThreadA取值:1467118842566
ThreadA取值:1467118842566
ThreadA取值:1467118842566

类InheritableThreadLocal的使用

使用类InheritableThreadLocal可以在子线程中获得父线程继承下来的值。

public class InheritableThreadLocalExt extends InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }
}

public class Tools {
    public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}

public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println("ThreadA取值:" + Tools.t1.get());
                Thread.sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println("Main取值:" + Tools.t1.get());
                Thread.sleep(100);
            }
            Thread.sleep(5000);
            ThreadA a = new ThreadA();
            a.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制台打印结果如下:

Main取值:1467119046785
Main取值:1467119046785
Main取值:1467119046785
Main取值:1467119046785
Main取值:1467119046785
ThreadA取值:1467119046785
ThreadA取值:1467119046785
ThreadA取值:1467119046785
ThreadA取值:1467119046785
ThreadA取值:1467119046785
值继承再修改

将上面带做如下修改:

public class InheritableThreadLocalExt extends InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }
    
    @Override
    protected Object childValue(Object parentValue) {
        return parentValue + "我是在子线程中加的~~~";
    }
}

重新运行程序,控制台打印结果如下:

Main取值:1467119256537
Main取值:1467119256537
Main取值:1467119256537
Main取值:1467119256537
Main取值:1467119256537
ThreadA取值:1467119256537我是在子线程中加的~~~
ThreadA取值:1467119256537我是在子线程中加的~~~
ThreadA取值:1467119256537我是在子线程中加的~~~
ThreadA取值:1467119256537我是在子线程中加的~~~
ThreadA取值:1467119256537我是在子线程中加的~~~

需要注意的是,如果子线程在取得值的同时,主线程将InheritableThreadLocal中的值进行更改,那么子线程取到的值还是旧值。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
应用背景为变电站电力巡检,基于YOLO v4算法模型对常见电力巡检目标进行检测,并充分利用Ascend310提供的DVPP等硬件支持能力来完成流媒体的传输、处理等任务,并对系统性能做出一定的优化。.zip深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值