*进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
*线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。
*一个进程中至少有一个线程。
*Java VM启动的时候会有一个进程java.exe. 该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
在程序还运行时,用过的资源会被回收,说明jvm启动不只一个线程,还有负责垃圾回收机制的线程。
**无法从静态上下文中引用非静态??:静态函数要用类.函数引用。非静态函数要先建立对象,再用对象调用,对象.函数。
*当用是static定义函数或者变量时,记住其
他函数调用它时就要用:class.函数。要是没有static,用对象调用。即:对象.函数。
*java已经提供了对线程这类事物的描述。就是Thread类。 创建自定义线程的第一种方式:继承Thread类。 步骤:1.定义类继承Thread。2.复写Thread类中的run方法。3.调用线程的start方法,该方法有两个作用:启动线程,调用run方法。(直接调用run方法的话,自定义线程没有启动,run方法就在主线程中执行,和普通方法一样。)
*Thread类线程都有自己的默认名称,Thread-编号 该编号从0开始. 自定义名称:class Test extends Thread { Test (String name){super(name);}}还可以用setNmae()方法定义名称。
*名称的作用是方便查看正在运行的线程是哪一个。
*Thread.currentThread().getName()可以得到当前线程对象的名称。currentThread()返回线程对象的引用。
*同一个线程不能重复启动。
**想写构造方法?看看构造方法的定义。 1,构造方法没有返回值,所以不用写void 2,构造方法名必须与类名相同, 3,构造方法里如果调用父类的构造方法,那么必须放在构造方法的第一行 提示错误的原因是,你在一个普通方法里调用了父类的构造方法
**不能在函数中直接定义一个新的函数,要想定义,只能在一个函数中定义一个类,再在类中定义函数。
*在售票的例子中,第一种定义的线程可以将私有数据定义为静态,实现多个线程共有一个数据。若不用静态方法呢,下面还有第二种定义线程的方法:实现Runnable接口。
步骤: 1,定义类实现Runnable接口; 2,覆盖Runnable接口中的run方法。(将线程要运行的代码存放在该run方法中)
3,通过Thread类建立线程对象,并将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。即Thread
demo=Thread(Runnable
a)(为什么要将Runnable接口的子类对象传递给Thread的构造函数?因为,自定义的run方法所属的对象是Runnable接口的子类对象,要让线程执行指定对象的run方法,就必须明确该run方法所属对象)
4,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
*通过第二种定义方式,将线程和所要执行的代码分离。可以在启动多个线程执行同一个方法。
*为什么要用接口的方法呢?因为java中不支持多继承,如果一个类中有需要多线程执行的代码,但是这个类已经继承了一个父类,那它就没办法再继承Thread类了。这点也说明,接口某种程度也是java多继承的补充。
*实现方式和继承方式的区别:运行代码存放的地方不同。 实现方式的好处是避免了单继承的局限性,在定义线程时,建议使用实现方式。
**接口的实现类中接口的方法有异常时不能往外抛,只能自己处理。
*售票的例子中,当在run代码中添加休息语句,将线程冻结时,结果出现了负数。多线程的运行出现了安全问题。 问题的原因: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完另一个线程参与进来,导致共享数据的错误。
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
*java对于多线程的安全问题提供了专业的解决方式。就是同步代码。synchronized(对象){需要被同步的代码(需要使用共享数据的代码就是需要被同步的代码)}当一个线程进入代码块中时,首先判断对像,是1时进入并把1变成0,若是0就无法进入,执行完后出来并把0变成1。synchronized(对象)对象如同锁,同步锁。没有通过锁的线程无法执行同步代码。
*同步的前提:1,必须有两个或两个以上的线程。2,必须是多个线程使用同一个锁。 必须保障同步中只能有一个线程在运行。 好处:解决了多线程的安全问题。弊端:多个线程需要判断锁,较为耗费资源。
*同步函数就是在函数类型前加一个synchronized标签,同步函数内部的代码一定要都是同步代码!同步函数的锁,就是this,函数需要被对象引用,那么函数都有一个所属对象引用,就是this。
*如果同步函数被静态修饰后,使用的锁是什么呢?通过验证,发现不再是this,因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class
该对象的类型是class。 静态的同步方法使用的锁是该方法所在类的字节码文件对象。也就是类名.class。
*死锁:当一个同步函数中有一个同步代码块。而这个代码块中也有这个同步函数,同步函数要同步代码块的锁,同步代码块要同步函数的锁,结果就是谁也执行不了了。就长生死锁了。当然,有时也会和谐,两个都执行了。编程时要避免死锁。
*线程间通信:其实就是多个线程在操作同一个资源。但是操作的动作不同。
*线程间通信的安全问题:将操作的资源设成同步代码块,就可以避免一个线程操作一半时就被另一个线程打断,从而使资源混乱。
*多线程等待唤醒机制: 在同一个资源中加入一个开关变量,一个线程认一种状态(开或关),谁对谁才可以执行,另外,当一个线程执行完后将开关变量转换状态,也就只能另一个线程来执行了,且此时还要完成以下动作:冻结该线程,唤醒另一线程。
*线程被冻结之后,就进入到一个叫做线程池的地方,调用唤醒函数时,就从线程池中按顺序唤醒线程。
*wait(),notify(),notifyAll(),都使用在同步中,因为要对持有锁(监视器)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
这些操作线程的方法都定义在Object类中:因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有在同一个锁上的被等待线程可以被同一个锁上的notify唤醒。不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
*if(flog)try(this.wait();)catch(exception e){}。。。。。//这条语句使线程在判断了标签flog之后冻结,当线程再次被唤醒后将直接执行后面的代码。但这样会出错,所以要把if(flog)换成while(flog)就可以解决问题了,因为while是循环语句,当线程再次唤醒后还在循环内,会再次判断flog,为假是时才会跳出循环,执行后面的代码。
*当有多个线程时,要用while(flog),和notifyAll()(例子生产者消费者) 对于多个生产者和消费者,为什么要定义while判断标记。因为要让被唤醒的线程在一次判断标记。
为什么定义notifyAll,因为要唤醒对方线程。只用notify容易至唤醒本方线程的情况,导致程序中的所有线程都等待。
JDK1.5中提供了多线程升级解决方案。将同步synchronized替换成lock操作。将Object中的wait,notify等替换成Condition对象。该对象可以lock锁进行获取。该方案可以实现本方只唤醒对方的操作。
*守护线程:setDaemon()。可以理解为后台线程,后台线程的运行依赖前台线程,(未标记为守护线程的就是前台线程)当前台线程结束后,后台线程也跟着结束。
*join方法:当A线程运行时遇到B.join();时,A线程就会被冻结并放出执行权(只是放出,并没有把执行权给B,至于给谁,又cpu随机决定),等B线程执行完之后A才会被自动唤醒。并参与执行。
*yield方法:暂停当前正在执行的线程对象,
*并执行其他线程。短暂暂停一下后自动参与执行。可以让各线程频繁的交换执行。