进程:后台运行的程序
线程:轻量级的进程
并发:指应用能够交替执行不同的任务,其实并发有点类似于多线程的原理,多线程并非是同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不断去切换这两个任务,以达到"同时执行效果",其实并不是的,只是计算机的速度太快,我们无法察觉到而已.
理解:就类似于你,吃一口饭喝一口水,以正常速度来看,完全能够看的出来,当你把这个过程以n倍速度执行时…可以想象一下, 吃->喝…如此反复.
并行:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边打电话,这两件事情可以同时执行
两者区别:一个是交替执行,一个是同时执行
class Ticket{
private int number=30;
List list=new ArrayList();
Lock lock=new ReentrantLock();
public void sale(){
lock.lock();
try {
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出第"+number--+"个"+"还剩"+number+"个");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class SaleTicketDemo01 {
public static void main(String[] args) {
Thread t1 = new Thread();
Thread t2 = new Thread();
Thread t3 = new Thread();
Ticket ticket = new Ticket();
// new Thread(new Runnable() {
// @Override
// public void run() {
// for (int i = 1; i < 40; i++) {
// ticket.sale();
// }
// }
// },"AAA").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 40; i++) {
ticket.sale();
}
}
},"BBB").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i < 40; i++) {
ticket.sale();
}
}
},"CCC").start();
new Thread(()->{
for (int i = 1; i < 40; i++) {
ticket.sale();
}
},"AAA").start();
new Thread(()->{
for (int i = 1; i <40 ; i++) {
ticket.sale();
}
},"BBB").start();
new Thread(()->{
for (int i = 1; i <40 ; i++) {
ticket.sale();
}
},"CCC").start();
}
}
使用到了java8的新特性:
interface Foo{
public int add(int x,int y);
public default int mul(int x,int y){
return x*y;
}
public static int div(int x,int y){
return x/y;
}
}
public class lambdaExpressDemo02 {
public static void main(String[] args) {
Foo foo=new Foo() {
@Override
public int add(int x, int y) {
return x+y;
}
};
int res = foo.add(1, 2);
System.out.println(res);
System.out.println(foo.mul(4,5));
System.out.println(Foo.div(6,2));//接口中的div使用的是static修饰的
Foo foo1=(int x,int y)->{
return x+y;
};
int res1 = foo1.add(3, 4);
System.out.println(res1);
}
}
java8中,接口中可以定义default修饰的方法,并且可以实现其方法体。
1、实现类会继承接口中的default方法
2、如果一个类同时实现接口A和B,接口A和B中有相同的default方法,这时,该类必须重写接口中的
default方法
为什么要重写呢?是因为,类在继承接口中的default方法时,不知道应该继承哪一个接口中的default方
法。
3、如果子类继承父类,父类中有b方法,该子类同时实现的接口中也有b方法(被default修饰),那么子
类会继承父类的b方法而不是继承接口中的b方法
List set map都是线程不安全的
New ArrayList()的初始容量是10 第一次扩容至 15 第二次扩容至 22
Hashmap()的初始容量是16 扩容是2^n
扩容方式ArrayList.copyof();
但是new Arraylist()存在扩容机制 list的数组中存放是的object类型的数据
ArrayList是线程不安全的
Case:
Arraylist()线程不安全的,但是效率高,并发性好
Vector()线程安全的,但是并发性较差
解决方法:New ArrayLis() new vector() new CopyWriteArrayList()
故障现象:java.util.coCurrentException
优化建议:
对象锁 synchronized实现同步的基础:Java中的每一个对象都可以作为锁
对于普通方法锁是当前的实例对象
对于同步方法块,锁是synchronized括号里的对象
对于静态同步方法,锁是当前类的Class对象
类的资源锁static
笔记:一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用一个
synchronized方法,其它的线程只能等待,换句话说,某一个时刻内,只有唯一一个线程去访问这些
synchronized方法。
锁的是当前对象this,被锁定后,其它的线程都不能进入当前对象的synchronized方法
加个普通方法发现和同步锁没有关系
换成两个对象后u,不是同一把锁,情况就发生了变化
所有的静态同步方法,用的是同一把锁–类对象本身
这两把锁是不同的对象,所以不会存在竞争的
但是一旦一个静态同步方法获得锁后,其它的静态同步方法必须等待该方法释放锁之后才能够获得锁。
而不管是同一个实例对象的静态同步方法之间还是不同的实例对象的静态同步方法之间,只要他们同一个类
的实例对象!
//资源类
class Phone{
public synchronized void sendMS() throws Exception{
System.out.println("send ms");
}
public synchronized void sendEmail() throws Exception{
System.out.println("send Email");
}
}
public class lock8Demo {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
try {
phone.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
phone.sendMS();
} catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
题目:现在有两个线程,可以操作初始值为零的一个变量,实现一个线程对该对象加1,一个线程对该对象减1,交替进行10轮,变量的初始值为0
1.高内聚,低耦合 线程操作资源类
2.判断,工作,通知
3,防止虚假唤醒
class AirCondition{
private int number=0;
public synchronized void increment() throws Exception{
if(number!=0){
wait();
}else{
number++;
}
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
public synchronized void decrement()throws Exception{
if(number==0){
wait();
}else{
number--;
}
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
}
public class ProdConsumerDemo {
public static void main(String[] args) {
AirCondition ac1 = new AirCondition();
AirCondition ac2 = new AirCondition();
new Thread(()->{
for (int i = 1 ; i <10 ; i++) {
try {
ac1.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"ac1").start();
new Thread(()->{
for (int i = 1 ; i <10 ; i++) {
try {
ac1.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"ac2").start();
}
}
多线程的交互问题不能使用if,需要使用while(循环+判断)
新写法
class AirCondition1{
private int number=0;
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
public void increment(){
try {
lock.lock();
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement(){
try {
lock.lock();
while (number!=1){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ProdConsumerDemo2 {
public static void main(String[] args) {
AirCondition1 airCondition1 = new AirCondition1();
new Thread(()->{
for (int i = 0; i < 10; i++) {
airCondition1.increment();
}
},"AAA").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
airCondition1.decrement();
}
},"BBB").start();
}
}
class ShareData{
private int number=1;
Lock lock=new ReentrantLock();
public Condition condition1=lock.newCondition();
public Condition condition2=lock.newCondition();
public Condition condition3=lock.newCondition();
public void print5(){
lock.lock();
try {
while(number!=1){
condition1.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
number=2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();//不要忘记关锁,每个对象用完锁之后都要进行释放
}
}
public void print10(){
lock.lock();
try {
while(number!=2){
condition2.await();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
number=3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void print15(){
lock.lock();
try {
while(number!=3){
condition3.await();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ConditionDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(()->{
shareData.print5();
},"AAA").start();
new Thread(()->{
shareData.print10();
},"BBB").start();
new Thread(()->{
shareData.print15();
},"CCC").start();
}
}
思想:用完锁之后不要忘记关闭锁,这是关键,这里使用了同一把锁,算是配了三把钥匙。
class myThread implements Runnable{
@Override
public void run() {
}
}
class myThread1 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("*******come in call method");
return 1024;
}
}
public class CallableDemo {
public static void main(String[] args) throws Exception {
FutureTask<Integer> task = new FutureTask<Integer>(new myThread1());
new Thread(task,"A").start();
Integer integer = (Integer) task.get();
System.out.println(integer);
}
}
JVM是运行在操作系统之上的,它与硬件没有直接的交互。
JVM的体系结构
类装载器ClassLoader
负责加载class文件,class文件在文件开头有特定的文件标示,将class文件字节码内容加载到内存中,
并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于它是否可
以运行,则由Execution Engine决定 。
public class MyObject {
public static void main(String[] args) {
Object obj = new Object();
System.out.println(obj.getClass().getClassLoader());
MyObject myObject = new MyObject();
System.out.println(myObject.getClass().getClassLoader());
//jdk.internal.loader.ClassLoaders$AppClassLoader@3fee733d
//自己编写的走APPloader这个加载器
}
}
Java一共有4个加载器
1.虚拟机自带的加载器
2.启动类加载器(Bootstrap)C++(java自带的类。环境比如Arraylist类等)根加载器
3.扩展类加载器(Extension)Java(拓展比如1.0到2.0...)
4.应用程序类加载器(AppClassLoader)Java也叫系统类加载器,加载当前应用的classpath的所有类
(自己编写的类)
用户自定义加载器 Java.lang.ClassLoader的子类,用户可以定制类的加载方式
双亲委派(不能污染java的源代码)
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一
个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己
无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加
载。
采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载
这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是
同样一个 Object对象。
思路:因为类的启动加载是从上往下 的。在java.lang.String 没有找到main方法 所以确实不存在main方法。
灰色是线程私有,红色是线程共享
Native Interface本地接口(私有的)
本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++程序,Java 诞生的时候是
C/C++横行的时候,要想立足,必须有调用 C/C++程序,于是就在内存中专门开辟了一块区域处理标记为
native的代码,它的具体做法是 Native Method Stack中登记 native方法,在Execution Engine
执行时加载native libraies。
程序计数器(私有的):就是一个PC指针,标识着程序运行到哪个方法(类似排班值日表)
方法区(method Area)共享的
But
实例变量存在堆内存中,和方法区无关
方法区
它存储了每一个类的结构信息
List list=new ArraysList();
方法区是规范,在不同虚拟机里面实现不一样,最典型的是永久代和元空间
f=new 永久代
f=new 元空间
Stack
栈管运行、堆管存储
程序=算法+数据结构
程序=框架+业务逻辑
队列(FIFO)
栈(先进后出)
栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈
内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线
程私有的。8种基本类型的变量+对象的引用变量+实例方法都是在函数的栈内存中分配。
栈存储什么内容?
栈帧中主要保存3 类数据:
本地变量(Local Variables):输入参数和输出参数以及方法内的变量;
栈操作(Operand Stack):记录出栈、入栈的操作;
栈帧数据(Frame Data):包括类文件、方法等等。
StackOverFlow属于错误
栈+堆+方法区交互关系
HotSpot(JDK)是使用指针的方式来访问对象 对象的模板
Java堆中存放访问类元数据的地址(模板,类的结构)
Reference存储的就是直接是对象的地址
Heap堆
一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方
法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:
Young Generation Space 新生区 Young/New
Tenure generation space 养老区 Old/ Tenure
Permanent Space 永久区 Perm
一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方
法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。
新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新
生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace) ,所有的类都是在伊甸区被
new出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空
间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中
的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。若幸存 0区也满
了,再对该区进行垃圾回收,然后移动到 1 区。那如果1 区也满了呢?再移动到养老区。若养老区也满
了,那么这个时候将产生MajorGC(FullGC),进行养老区的内存清理。若养老区执行了Full GC之后发
现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。
public class Test {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());//处理器的核数
long maxMemory = Runtime.getRuntime().maxMemory() ;//返回 Java 虚拟机试图使用的最大内存量。
long totalMemory = Runtime.getRuntime().totalMemory() ;//返回 Java 虚拟机中的内存总量。
System.out.println("MAX_MEMORY = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
System.out.println("TOTAL_MEMORY = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");
}
}