Java基础(八)——字符流、编码表、线程

目录

字符流

为什么会出现字符流

编码表

小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码

字符流中编码解码

字符缓冲流特有功能

IO小结

线程

多线程的实现方式

Class Thread

扫描二维码关注公众号,回复: 10037283 查看本文章

方式1:继承Thread类

两个小问题:

线程调度

线程控制

线程同步

案例:卖票

卖票案例数据安全问题的解决


字符流

一个汉字存储:
如果是GBK编码,占用2个字节
如果是UTF- 8编码,占用3个字节

为什么会出现字符流

由于字节流操作中文不是特别的方便,所以ava就提供字符流
字符流=字节流+编码表
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成
中文,如何识别是中文的呢?

  • 汉字在存储的时候, 无论选择哪种编码存储,第一个字节都是负数

编码表

基础知识:

  • 计算机中储存的信息都是用二 进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
  • 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。这里强调一下:按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象

字符编码:就是一套自然语言的字符与二 进制数之间的对应规则(A.65)

小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码

字符流中编码解码

字符流抽象基类

  • Reader: 字符输入流的抽象类
  • Writer: 字符输出流的抽象类

字符流中和编码解码问题相关的两个类:

  • InputStreamReader
  • OutputStreamWriter

InputStreamReader:是从字节流到字符流的桥粱

  • 它读取字节,并使用指定的编码将其解码为字符
  • 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

OutputStreamWriter:是从字符流到字节流的桥梁

  • 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
  • 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

代码:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class ZiFu {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\eclipse\\API文档\\File.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        osw.write("中国加油");
        osw.close();
    }
}

字符缓冲流特有功能

BufferedWriter:

  • void newLine():写- -行行分隔符, 行分隔符字符串由系统属性定义

BufferedReader:

  • public String readLine(): 读-行文字。 结果包含行的内容的字符串, 不包括任何行终止字符,如果流的结尾已经到达,则为null

代码:

public class ZiFu {
    public static void main(String[] args) throws IOException {
//        //创建字符缓冲输出流
//        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\eclipse\\API文档\\File.txt"));
//        //写数据
//        for (int i = 0; i < 10; i++) {
//            bw.write("hello" + i);
//            bw.write("\r\n");
//            bw.newLine();
//            bw.flush();
//        }
//        //释放资源
//        bw.close();
        //创建字符缓冲输入流
        BufferedReader br = new BufferedReader(new FileReader("D:\\eclipse\\API文档\\File.txt"));
        String by;
        while ((by=br.readLine())!=null){
            System.out.print(by);
        }
        br.close();

    }
}

结果:

IO小结

线程

线程:是进程中的单个顺序控制流,是一条执行路径

  • 单线程: 一个进程如果只有一条执行路径, 则称为单线程程序
  • 多线程: 一个进程如果有多条执行路径,则称为多线程程序

多线程的实现方式

Class Thread

  • public class Thread
    extends Object
    implements Runnable
    线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。

    每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。

    当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为main的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:

    创建一个新的执行线程有两种方法。 一个是将一个类声明为Thread的子类。 这个子类应该重写run类的方法Thread 。 然后可以分配并启动子类的实例。

    • 已经调用了Runtime类的exit方法,并且安全管理器已经允许进行退出操作。
    • 所有不是守护进程线程的线程都已经死亡,无论是从调用返回到run方法还是抛出超出run方法的run
  • 另一种方法来创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。

方式1:继承Thread类

  • 定义一个类MyThread继承Thread类
  • 在MyThread类中重写run()方法
  • 创建MyThread类的对象
  • 启动线程

两个小问题:

  • 为什么 要重写run()方法?
    • 因为run()是用来封装被线程执行的代码
  • run()方法和start()方法的区别?
    • run():封装线程执行的代码,直接调用,相当于普通方法的调用
    • start():启动线程;然后由JVM调用此线程的run()方法

测试代码:

public class MyThread {
    public static void main(String[] args) {
        //两个线程
        ThreadDemo my1 = new ThreadDemo();
        ThreadDemo my2 = new ThreadDemo();
        //此时并没有多线程执行,还是单线程执行
//        my1.run();
//        my2.run();
        //start()  导致此线程开始执行; Java虚拟机调用此线程的run方法。
        my1.start();
        my2.start();
    }
}
/*
定义一个类MyThread继承Thread类
在MyThread类中重写run()方法
创建MyThread类的对象
启动线程
*/
public class ThreadDemo extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+ ":" + i);
        }
    }
}

结果:

Thread类中设置和获取线程名称的方法

  • void setName(String name):将此线程的名称更改为等于参数name
  • String getName():返回此线程的名称

线程调度

线程有两种调度模型

  • 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
  • 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择个, 优先级高的线程获取的CPU时间片相对多- -些

Java使用的是抢占式调度模型
假如计算机只有一个CPU, 那么CPU在某一个时刻只能执行一 条指令 ,线程只有得到CPU时间片,也就是使用权,
可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的

public final int getPriority(): 返回此线程的优先级
System . out. println(tp1. getPriority()); 
public final void setPriority(int newPriority): 更改此线程的优先级
tp1. setPriority(10000);

线程优先级最高10,最低1,默认是5

线程控制

代码:线程暂停1S

public class ThreadDemo extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+ ":" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class MyThread {
    public static void main(String[] args) {
        //两个线程
        ThreadDemo my1 = new ThreadDemo();
        ThreadDemo my2 = new ThreadDemo();
        ThreadDemo my3 = new ThreadDemo();

        my1.setName("曹操");
        my2.setName("刘表");
        my3.setName("孙策");

        my1.start();
        my2.start();
        my3.start();
    }
}

结果: 

线程同步

案例:卖票

需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

思路:
①义一个类SelITicket实现Runnable接口, 面定义一个成员变量: private int tickets= 100;
②在SellTicket类中写run0方法实现卖票,代码步骤如下

  • A:判断票数大于0,就卖票,并告知是哪个窗口卖的
  • B:卖了票之后,总票数要减1
  • C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行

③定义一个测试类SellTicketDemo, 里面有main方法,代码步骤如下

  • A:创建SellTicket类的对象
  • B:创建三个Thread类的对象,把SelITicket对象作为构造方法的参数,并给出对应的窗口名称
  • C:启动线程
package cn.itcast.day8.买票;

public class SellTicket implements Runnable {
    private int tickets = 100;

    @Override
    public void run() {
//        A:判断票数大于0,就卖票,并告知是哪个窗口卖的
//        B:卖了票之后,总票数要减1
//        C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
        //问题:相同的票出现了多次  ---原因:线程执行的随机性导致的
        boolean flag = true;
        while (flag == true){
            if (tickets > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //t1抢到了CPU的执行权,在控制台输出,窗口1出售1张票
                //假设t1继续拥有CPU执行器,就会执行tickets--;操作
                System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
                tickets--;
            }
            else {
                System.out.println("票已经卖完了");
                flag = false;
            }
        }

    }
}
package cn.itcast.day8.买票;

public class SellTicketDemo {
    public static void main(String[] args) {
//        A:创建SellTicket类的对象
//        B:创建三个Thread类的对象,把SelITicket对象作为构造方法的参数,并给出对应的窗口名称
//        C:启动线程
        SellTicket st = new SellTicket();

        Thread t1 = new Thread(st,"窗口1");
        Thread t2 = new Thread(st,"窗口2");
        Thread t3 = new Thread(st,"窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

卖票案例数据安全问题的解决

为什么出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准

  • 是否是多线程环境
  • 是否有共享数据
  • 是否有多条语句操作共享数据

如何解决多线程安全问题呢?

  • 基本思想: 让程序没有安全问题的环境

怎么实现呢?

  • 把多条语句操作共享 数据的代码给锁起来,让任意时刻只能有一个线程执行即可
  • java提供了同步代码块来实现

一起学习,一起进步 -.- ,如有错误,可以发评论

发布了214 篇原创文章 · 获赞 52 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_36171287/article/details/104827683
今日推荐