java基础巩固8

对称加密算法

(1)对称加密算法就是传统的用一个密码进行加密和解密。例如,我们常用的WinZIP和WinRAR对压缩包的加密和解密,就是使用对称加密算法。从程序的角度看,所谓加密,就是一个接受密码和明文,然后输出密文的函数,而解密则相反,它接收密码和密文,然后输出明文。在软件开发中,常用的对称加密算法有:
在这里插入图片描述
密钥长度直接决定加密强度,而工作模式和填充模式可以看成是对称加密算法的参数和格式选择。Java标准库提供的算法实现并不包括所有的工作模式和所有填充模式,但是通常我们只需要挑选常用的使用就可以了。
DES算法由于密钥过段,现在可以再短时间内暴力破解,已经不安全了。

多线程基础

(1)进程和线程是包含关系,但是多任务既可以由多进程实现,也可以由单进程内的多线程实现,还可以混合多进程+多线程。
具体采用哪种方式,要考虑到进程和线程的特点。
和多线程相比,多进程的缺点在于:
创建进程比创建线程开销大,尤其是在windows系统上;
进程间通信比线程间通信要慢,因为线程间的通信就是读写同一个变量,速度很快。
多进程的优点在于:
多进程稳定性比多线程高,因为在多进程的情况下,一个进程不会影响其他进程,而在多线程的情况下,任何一个线程崩溃会导致整个进程崩溃。
(2)java语言内置了多线程支持:一个java程序实际上是一个jvm进程,jvm进程用一个主线程来执行main方法,在main()方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程。
(3)要特别注意,直接调用Thread实例的run()方法是无效的,直接调用run()方法,相当于调用了一个普通的java方法,当前线程并没有任何改变,也不会启动新线程。必须调用Thread实例的start()方法才能启动新线程,如果我们产看Thread类的源代码,会看到start()方法内部调用了一个private native void start0()方法,native修饰符表示这个方法是由JVM虚拟机内部的C代码实现的,不是由java实现的。
(4)可以对线程设定优先级,设定优先级的方法是Thread.setPriority(int n),优先级高的线程被操作系统调度的优先级较高,操作系统对高优先级线程可能调度更频繁,但我们不能通过设置优先级来确保高优先级的线程一定会先执行。

线程的状态

(1)在java程序中,一个线程对象只能调用一次start()方法启动新线程,并在新线程中执行run()方法,一旦run()方法执行完毕,线程就结束了。因此,java现成的状态有以下几种:
New:新创建的线程,尚未执行
Runnable:运行中的线程,正在执行run()方法的java代码
Blocked:运行中的线程,因为某些操作被阻塞而挂起
waiting:运行中的线程,因为某些操作在等待中
Time Waiting:运行中的线程,因为执行sleep()方法正在计时等待
Terminated:线程已经终止,因为run()执行完毕
(2)线程终止的原因有:
一、线程正常终止:run()方法执行到return语句返回
二、线程意外终止:run()方法因为未捕获的异常导致线程终止;
三、对某个线程Thread实例调用stop()方法强制终止(强烈不推荐使用)。
(3)一个线程还可以等待另一个线程知道其运行结束,例如,main线程在启动t线程后,可以通过t.join()等待t线程结束后再继续运行。当main线程对线程对象t调用join()方法时,主线程将等待变量t表示的线程运行结束,即join就是指等待该线程结束,然后才继续往下执行自身线程。如果t线程已经结束,对实例t调用join()会立刻返回。此外,join(long)的重载方法也可以指定一个等待时间,超过等待时间后就不再继续等待。

中断线程

(1)如果线程需要执行一个长时间任务,就可能需要中断线程,中断线程就是其他线程给该线程发一个信号,该线程收到信号后立刻结束执行run()方法,是的自身线程能立刻结束运行。
(2)main线程通过调用t.interrupt()从而通知t线程中断,而此时t线程正位于hello.join的等待中,此方法会立即结束等待并抛出InterruptedException。由于我们在t线程中捕获了InterruptedException,因此就可以准备结束该线程。而t线程结束前,对hello线程也进行了interrupt()通知其中断。
(3)另一个常用的中断线程的方法是设置标志位。我们通常会用一个running标志位来标识线程是否应该继续运行。在外部线程中,通过吧HelloThread.running设置为false,就可以让线程结束。
(4)HelloThread的标志位boolean running是一个线程间共享的变量,线程间共享变量需要使用volatile关键字标识,确保每个线程都能读取到更新后的值。为什么要对线程间共享的变量用关键字volatile声明?这涉及到Java的内存模型,在java虚拟机中,变量的值保存在主内存中,但是,当线程访问变量时,它会先获取一个副本,并保存在自己的工作内存中。如果线程修改了变量的值,虚拟机会在某个时刻把修改后的值写回主内存,但是这个时间是不确定的。
在这里插入图片描述
这会导致如果一个线程更新了某个变量,另一个线程读取的值可能还是更新前的。因此,volatile关键字的目的是告诉虚拟机:
每次访问变量时,总是获取主内存的最新值;
每次修改变量后,立刻会写到主内存。
volatile关键字解决的是可见性问题,当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值,如果我们去掉volatile关键字,运行程序,发现效果差不多,这是因为在x86的架构下,JVM回写主内存的速度非常快,但换成ARM的架构,就会有显著的延迟。

计算机架构简介

(1)ARM架构,过去称作进阶精简指令集机器,是一个32位精简指令集处理器架构,其广泛地使用在许多嵌入式系统设计。由于节能的特点,ARM处理器非常适用于行动通讯领域。
(2)x86系列/Atom处理器,x86是Intel首先开发制造的一种微处理器体系结构的返程。x86架构是重要地可变指令长度复杂指令长度的CISC。
(3)PowerPC 是一种精简指令集(RISC)架构的中央处理器(CPU),PowerPC架构的特点是可伸缩性好、易嵌入、方便灵活、能耗低。PowerPC 处理器有广泛的应用范围(如任天堂 Gamecube 使用了 PowerPC)。

守护线程

(1)java程序入口就是JVM启动main线程,main线程又可以启动其他线程。当所有线程都运行结束时,jvm退出,进程结束。如果有一个线程没有退出,jvm进程就不会退出,所以必须保证所有线程都能及时结束。但是有一种线程的目的就是无限循环。例如一个定时触发任务的线程,如果这个线程不结束,JVM进程就无法结束。当其他线程结束时,JVM进程又必须结束,怎么办?答案是守护线程(Daemon Thread),守护线程是指为其他线程服务的线程。在jvm中,所有非守护线程都结束后,无论有没有守护线程,虚拟机都会自动退出。
(2)在守护线程中,编写代码要注意,守护线程不能持有任何需要关闭的资源,例如打开文件等,因为虚拟机退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失。

线程同步

(1)当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。此时如果多个线程同时读写共享变量,会出现数据不一致的问题。因此在多线程模式下,要保证逻辑正确,对共享变量进行读写时,必须保证一组指令以原子方式执行,即某一个线程执行时,其他线程必须等待。通过加锁和解锁的操作,就能保证3条指令总是在一个线程执行期间,不会有其他线程会进入此指令区间。即使在执行期线程被操作系统中断执行,其他线程也会因为无法获得锁导致无法进行此指令区间。只有执行线程将锁释放后,其他线程才有机会获得锁并执行。这种加锁与解锁之间的代码块我们称为临界区,任何时候临界区最多只有一个线程能够执行。
(2)保证一段代码的原子性就是通过加锁和解锁实现的。java程序使用synchronized关键字对一个对象进行加锁,synchronized保证了代码块在任意时刻最多只有一个线程能够执行。
(3)
synchronized(Counter.lock) { // 获取锁

} // 释放锁
它表示用Count.lock实例作为锁,两个线程在执行各自的synchronized(Count.lock){…}代码块时,必须先获取锁,才能进入代码块进行。执行结束后,在synchronized语句块结束会自动释放锁。这样一来,对Counter.count变量进行读写就不可能同时进行。使用synchronized解决了多线程同步访问共享变量的正确性问题。但是,它的缺点是带来了性能下降。因为synchronized代码块无法并发执行。此外,加锁和解锁需要消耗一定的时间,所以,synchronized会降低程序的执行效率。
因此使用synchronized的流程为:
1.找出修改共享变量的线程代码块。
2.选择一个共享实例作为锁。
3.使用synchronized(lockObject){…}。
在使用synchronized的时候,不必担心抛出异常,因为无论是否有异常,都会在synchronized结束处正确释放锁。
(4)jvm规范定义了几种原子操作:
基本类型(long和double除外)赋值,例如:int n = m
引用类型赋值,例如List<String> list = anotherList;
long和double是64位数据,JVM没有明确规定64位赋值操作是不是一个原子操作,不过在x64平台的JVM是把long和double的赋值作为原子操作实现的。

同步方法

(1)如果一个类被设计为允许多线程正确访问,我们就说这个类是线程安全地(thread-safe)。Java标准库的java.lang.StringBuffer也是线程安全的。还有一些不变类,例如String,Integer,LocalDate,它们所有变量都是final,多线程同时访问时只能读不能写,这些不变类也是线程安全的。最后,类似Math这些只提供静态方法,没有成员变量的类,也是线程安全的。除了上述几种少数情况,大部分类,例如ArrayList,都是非线程安全的类,我们不能再多线程中修改它们。但是,如果所有线程都只读取,不写入,那么ArrayList是可以安全地在线程间共享的。
(2)因此,用synchronized修饰的方法就是同步方法,它表搜狐和怎个方法都必须用this实例加锁。对于static方法,是没有this实例的,因为static方法是针对类而不是实例;但是我们注意到任何一个类都有一个由JVM自动创建的Class实例,因此,对static方法添加synchronized,锁住的是该类的Class实例。

死锁

(1)Java的线程锁是可重入的锁。对于同一个线程,能否在获取到锁以后继续获取同一个锁?答案是肯定的,JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫作可重入锁。由于Java的线程锁是可重入锁,所以获取锁的时候,不但要判断是否第一次获取,还要记录这是第几次获取。每获取一次锁,记录+1,每退出一次synchronized块,记录-1,减到0的时候,才会真正地释放锁。
(2)一个线程可以获取一个锁后,在继续获取另一个锁。在获取多个锁的时候,不同线程获取多个不同对象的锁可能会导致死锁。两个线程各自持有不同的锁,然后各自视图获取对方手里的锁,造成了双方无限等待下去,这就是死锁。死锁发生后,没有任何机制能解除死锁,只能强制结束JVM进程。
(3)如何避免死锁?线程获取锁的顺序要一致,即严格按照先获取lockA,再获取lockB的顺序。

使用wait和notify

(1)在Java程序汇总,synchronized解决了多线程竞争的问题。例如,对一个任务管理器,多个线程同时往队列中添加任务,可以使用sunchronized加锁。但是synchronized并没有解决多线程协调的问题。
(2)多线程协调运行的原则是:当条件不满足时,线程进入等待状态,当条件满足时,线程被唤醒,继续执行任务。wait()方法并不是一个普通的Java方法,而是定义在Object类的一个native方法,也就是由JVM的C代码实现的。其次,必须在sunchronized块中才能调用wait()方法,因为wati()方法调用时,会释放线程所获得的锁,wait()方法返回后,线程又会重新视图获取锁。因此,只能在锁对象上调用wait()方法。
(3)当一个线程在this.wati()等待时,它就会释放锁,从而是的其他线程可以在当前方法获得this锁。线程对this锁对象调用notify方法,这个方法会唤醒一个正在this锁等待的线程,从而使得等待线程从this.wait()方法返回。
(4)注意到wait()方法返回时需要重新获得this锁。假设当前有三个线程被唤醒,唤醒后,首先要等待执行当前方法的线程结束此方法后,才会释放this锁,随后这3个线程中只有一个获取到this锁,剩下两个将继续等待。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值