我的Java之路(7)

今天主要看了线程和输入输出流的知识。发现和操作系统有着千丝万缕的关系。。。。闲话少说,针对个人情况写写感觉重要的地方吧

java中的线程

以往我们开发的程序大多是单线程的,即一个程序只有一条从头到尾的执行线索。

java语言的一大特点就是内置对多线程的支持(java.lang包中的Thread类)。多线程是指同时存在几个执行体,按照几条不同的执行线索共同工作的情况,

使得编程人员可以很方便的开发出具有多线程的功能、能同时处理多个任务强大的应用程序。

虽然执行线程给人一种几个时间同时发生的个感觉,但这只是一种错觉,因为计算机在任何给定的时刻只能执行这些线程中的一个,java可以很快的控制从一个线程切换到另一个线程,

java虚拟机(JVM)负责管理这些线程这些线程将被轮流执行,使得每个线程都有机会使用CPU的资源。

程序是一段静态的代码,它是应用软件执行的蓝本。进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、
发展至消亡的过程。线程是比进程更小的执行单位。一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的产生、
存在和消亡的过程,也是一个动态的概念。
 
Java应用程序总是从主类的main方法开始执行。当JVM加载代码,发现main方法之后,就会启动一个线程,这个线程称作“主线程”,该线程负责执行main方法。
那么,在main方法中再创建的线程,就称为主线程中的线程。如果main方法中没有创建其他的线程,那么当main方法执行完最后一个语句,即main方法返回时,
JVM就会结束我们的Java应用程序。如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,
main方法即使执行完最后的语句,JVM也不会结束我们的程序,JVM一直要等到主线程中的所有线程都结束之后,才结束我们的Java应用程序。
 
线程的生命周期
(1)新建:
当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态,此时线程已经有了相应的内存空间和其他资源。
(2)运行:
线程创建后仅仅是占有了内存资源,在JVM管理的线程中还没有这个线程,此线程必须调用start()方法(从父类继承的方法)通知JVM,
这样JVM就会知道又有一个新一个线程排队等候切换了。
当JVM将CUP使用权切换给线程时,如果线程是Thread的子类创建的,该类中的run方法就立刻执行。所以我们必须在子类中重写父类的run方法,
Thread类中的run()方法没有具体内容,程序要在Thread类的子类中重写run()方法来覆盖父类的run()方法,run方法规定了该线程的具体使命。
    在线程没有结束run方法之前,不要让线程再调用start方法,否则将发生ILLegalThreadStateException异常。
(3)中断
中断有4种情况
1:
JVM将CPU资源从当前线程切换给其他线程,使本线程让出CPU的使用权处于中断状态。
 2:
线程使用CPU资源期间,执行了sleep(int millsecond)方法,使当前线程进入休眠状态。sleep(int millsecond)方法是Thread类中的一个类方法,
线程一旦执行了sleep(int millsecond)方法,就立刻让出CPU的使用权,使当前线程处于中断状态。经过参数millsecond指定的豪秒数之后,
该线程就重新进到线程队列中排队等待CPU资源,以便从中断处继续运行。
3:
线程使用CPU资源期间,执行了wait()方法,使得当前线程进入等待状态。等待状态的线程不会主动进到线程队列中排队等待CPU资源,
必须由其他线程调用notify()方法通知它,使得它重新进到线程队列中排队等待CPU资源,以便从中断处继续运行。有关wait、noftify和notifyAll方法将在第8节详细讨论
 4:
线程使用CPU资源期间,执行某个操作进入阻塞状态,比如执行读/写操作引起阻塞。进入阻塞状态时线程不能进入排队队列,只有当引起阻塞的原因消除时,
线程才重新进到线程队列中排队等待CPU资源,以便从原来中断处开始继续运行。
(4)死亡:
处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有二,一个是正常运行的线程完成了它的全部工作,即执行完run方法中的全部语句,结束了run方法。
另一个原因是线程被提前强制性地终止,即强制run方法结束。所谓死亡状态就是线程释放了实体,即释放分配给线程对象的内存。
 
线程的优先级和调度管理:
如果没有明显设置线程的优先级别,每个线程的优先级都为5(包括主线程)
java调度器的任务是是高优先级的线程能始终运行,一旦时间片有空闲,则使具有同等优先级的线程以轮流的方式顺序使用时间片。
 
Thread子类创建进程:
户可以扩展 Thread类,但需要重写父类的run方法,其目的是规定线程的具体操作,否则线程就什么也不做,因为父类的run方法中没有任何操作语句
使用Thread子类创建线程的优点是:我们可以在子类中增加新的成员变量,使线程具有某种属性,也可以在子类中新增加方法,使线程具有某种功能。
但是,Java不支持多继承,Thread类的子类不能再扩展其他的类。
 
Runnable接口:
创建线程的另一个途径就是用Thread类直接创建线程对象。使用Thread创建线程对象时,通常使用的构造方法是:
   Thread(Runnable target),
   该构造方法中的参数是一个Runnable类型的接口,因此,在创建线程对象时必须向构造方法的参数传递一个实现Runnable接口类的实例,
该实例对象称作所创线程的目标对象,当线程调用start方法后,一旦轮到它来享用CPU资源,目标对象就会自动调用接口中的run方法(接口回调),
这一过程是自动实现的,用户程序只需要让线程调用start方法即可,也就是说,当线程被调度并转入运行状态时,所执行的就是run()方法中所规定的操作
线程的常用方法:
1.start()
    线程调用该方法将启动线程,使之从新建状态进入就绪队列排队,一旦轮到它来享用CPU资源时,就可以脱离创建它的主线程独立开始自己的生命周期了
2.run()
    Thread类的run()方法与Runnable接口中的run()方法的功能和作用相同,都用来定义线程对象被调度之后所执行的操作,都是系统自动调用而用户程序不得引用的方法。
3.sleep(int millsecond) 
   优先级高的线程可以在它的run()方法中调用sleep方法来使自己放弃处理器资源,休眠一段时间。
 
4.isAlive()
    在线程的run方法结束之前,即没有进入死亡状态之前,线程调用isAlive()方法返回true,当线程进入死亡状态后(实体内存被释放),线程仍可以调用方法isAlive(),
这时返回的值是false。(线程未调用start方法之前,调用isAlive()方法返回false).
需要注意的是,一个已经运行的线程在没有进入死亡状态时,不要再给线程分配实体,由于线程只能引用最后分配的实体,先前的实体就会成为“垃圾”,并且不会被垃圾收集机收集掉.
5.currentThread()
    currentThread()方法是Thread类中的类方法,可以用类名调用,该方法返回当前正在使用CPU资源的线程。
6.interrupt()
    intertupt方法经常用来“吵醒”休眠的线程。当一些线程调用sleep方法处于休眠状态时,一个使用 CPU资源的其它线程在执行过程中,
可以让休眠的线程分别调用interrupt 方法“吵醒”自己,即导致休眠的线程发生InterruptedException异常,从而结束休眠,重新排队等待CPU资源。
 
线程同步:
线程同步是指多个线程要执行一个synchronized修饰的方法,如果一个线程A占有CPU资源期间,使得synchronized方法被调用执行,
那么在该synchronized方法返回之前,即synchronized方法调用执行完毕之前,其他占有CPU资源的线程一旦调用这个synchronized方法就会引起堵塞,
堵塞的线程要一直等到堵塞的原因消除( synchronized方法返回),再排队等待CPU资源,以便使用这个同步方法。
 
其他具体相关知识见P165页
 
 
输入与输出流:
I/O流中输入流的指向称为源,程序从指向源的输入流中读取源中的数据。
输出流的指向称为目的地,程序通过向输出流中写入数据把信息传递到目的地。
File的构造方法有3个:
n  File(String  filename);
n  File(String directoryPath,String    filename);
n  File(File f, String filename);
经常使用File类的下列方法获取文件本身的一些信息:
public String getName()  获取文件的名字。
public boolean canRead()  判断文件是否是可读的。
public boolean canWrite()  判断文件是否可被写入。
public boolean exits()  判断文件是否存在。
public long length()  获取文件的长度(单位是字节)。
public String getAbsolutePath()  获取文件的绝对路径。
public String getParent()  获取文件的父目录。
public boolean isFile()  判断文件是否是一个正常文件,而不是目录。
public boolean isDirectroy()  判断文件是否是一个目录。
public boolean isHidden()  判断文件是否是隐藏文件。
public long lastModified()  获取文件最后修改的时间(时间是从1970年午夜至文件最后修改时刻的毫秒数
 
   2.目录
 (1)创建目录
   public boolean mkdir():创建一个目录,如果创建成功返回true,否则返回false。
 (2)列出目录中的文件
      public String[] list()  用字符串形式返回目录下的全部文件,
  public File [] listFiles()  用File对象形式返回目录下的全部文件。
  public String[] list(FilenameFilter  obj)  该方法用字符串形式返回目录下的指定类型的所有文件。
  public File [] listFiles(FilenameFilter  obj)  该方法用File对象返回目录下的指定类型所有文件。
 
 3.文件的创建与删除
  当使用File类创建一个文件对象后,例如
  File f=new File("c:\\myletter","letter.txt");
  如果c:\myletter目录中没有名字为letter.txt文件,文件  对象f调用方法:
  public boolean createNewFile()
  可以在c:\myletter目录中建立一个名字为letter.txt的文  件。
  文件对象调用方法
  public boolean delete()
  可以删除当前文件,例如:
  f. delete();
 
4.运行可执行文件
  首先使用Runtime 类声明一个对象,如:
  Runtime  ec;
  然后使用静态getRuntime()方法创建这个对象:
  ec=Runtime.getRuntime();
  ec可以调用exec(String command)方法打开本地机的可执行文件或执行一个操作。
文件字节流:
1.FileInputStream类
2.FileOutputStream类
文件字符流:
1.FileReader类
2.FileWriter类
详见P179
从键盘读取数据(略);
 
缓冲流
BufferedReader类
 
  BufferedReader的构造方法是:
  BufferedReader(Reader in)
  BufferedReader流能够读取文本行,方法是readLine()。
通过向BufferedReader传递一个Reader对象(如FileReader的实例),来创建一个BufferedReader对象,如:
  FileReader  inOne=new FileReader(“Student.txt”);
  BufferedReader inTwo=new BufferedReader(inOne);
  然后inTwo调用readLine()顺序读取文件“Student.txt”的一行。
2.BufferedWriter类
  类似地,可以将BufferedWriter流和FileWriter流连接在一起,然后使用BufferedWriter流将数据写到目的地,例如:
  FileWriter  tofile=new FileWriter("hello.txt");
  BufferedWriter out=new BufferedWriter(tofile);
  BufferedReader流调用方法:
  write(String str)
  write(String s,int off,int len)
  把字符串s或s的一部分写入到目的地。
数组流
字符串流
数据流
对象流
序列化和对象克隆
随机读写流
以上详见P182~P188(Java大学实用教程(第二版))
文件锁
RondomAccessFile创建的流在读写文件时可以使用文件锁,那么只要不解除该锁,其它线程无法操作被锁定的文件。使用文件锁的步骤如下:
(1)首先使用RondomAccessFile流建立指向文件的流对象,该对象的读写属性必须是“rw”,例如:
  RandomAccessFile  input=new  RandomAccessFile("Example.java","rw");
(2)流对象input调用方法getChannel()获得一个连接到底层文件的FileChannel 对象(信道),例如
     FileChannel channel=input.getChannel();
(3)信道调用tryLock()或lock()方法获得一个FileLock(文件锁)对象,这一过程也称做对文件加锁,例如:
    FileLock lock=channel.tryLock();
 另外,FileInputStream以及FileOutputStream在读/写文件时都可以获得文件锁。
  在下面的例子13中Java程序在读取文件Example9_1.java时,使用了文件锁,这时你无法用其它程序来操作文件Example9_1.java,比如在Java程序结束前,你用Windows下的“记事本”(Notepad.exe)也无法修改、保存Example9_1.java。
(祥见P191)
 
本章完
 
 
 
 
 
 
原文地址:https://www.cnblogs.com/yanmingup/p/2879193.html