• 分布式-网络通信-线程(1)


    线程总结
    一.线程基础
    1. 什么是进程
    所谓进程就是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
    进程中所包含的一个或多个执行单元称为线程。进程还拥有一个私有的虚拟地址空间,该空间仅能被
    它所包含的线程访问。线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建
    一个进程后
    ,该进程会自动申请一个名为主线程或首要线程的线程。操作系统中有若干个线程在"同时"运行。通常,
    操作系统
    上运行的每一个应用程序都运行在一个进程中,例如:QQ,IE 等等。
    2.什么是线程
    一个线程是进程的一个顺序执行流。同类的多个线程共享一块内存空间和一组系统资源,线程本身有一
    个供程序执行时的堆栈。线程在切换时负荷小,因此,线程也被称为轻负荷进程。一个进程中可以包含
    多个线程。
    3. 进程与线程的区别
    一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行
    过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
    线程在执行过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出
    口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
    从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没
    有将多个线程看做多个独立的应用来实现进程的调度和管理以及资源分配。
    4. 线程使用的场合
    线程通常用于在一个程序中需要同时完成多个任务的情况。我们可以将每个任务定义为一个线程,使他
    们得以一同工作。
    例如我们在玩某个游戏时,这个游戏由操作系统运行,所以其运行在一个独立的进程中,而在游戏中我
    们会听到某些背景音乐,某个角色在移动,出现某些绚丽的动画效果等,这些在游戏中都是同时发生的,
    但实际上,播放音乐是在一个线程中独立完成的,移动某个角色,播放某些特效也都是在独立的线程中
    完成的。这些事情我们无法在单一线程中完成。
    也可以用于在单一线程中可以完成,但是使用多线程可以更快的情况。比如下载文件。
    比如迅雷,我们尝尝会开到它会打开很多个节点来同时下载一个文件。
    5. 并发原理
    多个线程或进程 同时 运行只是我们感官上的一种表现 ” ” 。
    原理:
    事实上进程和线程是并发运行的,OS 的线程调度机制将时间划分为很多时间片段(时间片),尽可能均
    匀分配给正在运行的程序,获取 CPU 时间片的线程或进程得以被执行,其他则等待。而 CPU 则在这些
    进程或线程上来回切换运行。
    微观上所有进程和线程是走走停停的,宏观上都在运行,这种都运行的现象叫并发,但是不是绝对意义
    上的 同时发生。 “
    二.创建线程
    1. 使用 Thread 创建线并启动线程
    Thread 线索,线程
    Thread 封装了操作系统对线程的调度过程。
    使用 Thread 类创建线程
    1. 继承 Thread 类,继承了复杂的线程管理功能。
    2. 重写 run 方法,就是并发执行的方法,线程执行时候,将执行修改以后的 run 方法。
    3. 用 start 方法,启动线程: 将线程交付给操作系统,由操作系统负责调度管理。操作系统会执行
    run 方法!
    注意:直接执行 run 方法不是线程执行了!
    线程代码
    class MyThread extends Thread{
    public void run(){
    System.out.println(
    "Hello World!");
    }
    }
    MyThread t = new MyThread();
    t.start();
    2. 使用 Runnable 创建并启动线程
    实现 Runnable 接口并重写 run 方法来定义线程体,然后在创建线程的时候将 Runnable 的实例传入并启动
    线程。
    1. 实现 Runnable 接口,实现 run 方法。
    2. 创建接口的子类实例。
    3. 创建线程对象,将接口的子类实例作为线程参数
    4. 调用 start 方法。启动线程
    案例:
    class MyRunner implements Runnable{
    public void run(){
    System.out.println("Hello World!");
    }
    }
    //启动线程
    MyRunner runner = new MyRunner();
    Thread t = new Thread(runner);
    t.start();
    使用 Runnable 接口创建线程的好处是,当前的类可以继承于其他的类,也可以继承其他的
    接口。
    三.线程操作 API
    1. Thread.currentThread 方法
    Thread 的静态方法 currentThread 方法可以用于获取运行当前代码片段的线程。
    Thread current = Thread.currentThread();
    2.获取线程信息
    Thread 提供了 获取线程信息的相关方法:
    • long getId():返回该线程的标识符
    • String getName():返回该线程的名称
    • int getPriority():返回线程的优先级
    • Thread.state getState():获取线程的状态
    • boolean isAlive():测试线程是否处于活动状态
    • boolean isDaemon():测试线程是否为守护线程
    • boolean isInterrupted():测试线程是否已经中断
    3. 线程优先级
    线程的切换是由线程调度控制的,我们无法通过代码来干涉,但是我们可以通过提高线程的优先级来最
    大程度的改善线程获取时间片的几率。
    线程的优先级被划分为 10 级,值分别为 1-10,其中 1 最低,10 最高。线程提供了 3 个常量来表示最低,
    最高,以及默认优先级:
    Thread.MIN_PRIORITY,
    Thread.MAX_PRIORITY,
    Thread.NORM_PRIORITY
    设置优先级的方法为:
    void setPriority(int priority)
    优先级高的线程,获得 CPU 时间片段多,执行机会多,但是在计算资源丰富的情况下,运算结果不明显
    案例:
    DemoThread t1 = new DemoThread();
    System.out.println(t1.getPriority());//5
    t1.str="A";
    t1.setPriority(1);
    DemoThread t2 = new DemoThread();
    t2.str = "B";
    DemoThread t3 = new DemoThread();
    t3.str = "C";
    t3.setPriority(10);
    t1.start();
    t2.start();
    t3.start();
    4. 守护线程(后台线程)
    当全部前台线程都结束时候, 如果后台线程还没有结束,这时候后台线程将被结束!
    后台线程可以用于后台背景音乐播放控制。
    • 使用 setDaemon(true) 将线程设置为后台线程
    • setDaemon 一定在线程启动之前调用,


    案例:
    public class Demo06 {
    public static void main(String[] args) {
    TestThread t1 = new TestThread();
    t1.setName("T1");
    t1.time = 3000;
    TestThread t2 = new TestThread();
    t2.setName("T2");
    t2.time = 5000;
    TestThread tx = new TestThread();
    tx.setName("TX");
    tx.time = 10000;
    //将 tx 设置为后台(精灵、守护)线程
    // setDaemon 一定在线程启动之前调用
    tx.setDaemon(true);
    /**
    * 当全部前台线程都结束时候, 如果后台
    * 线程还没有结束,这时候后台线程将被结束!
    */
    t1.start();
    t2.start();
    tx.start();
    System.out.println("Bye!");
    }
    }
    class TestThread extends Thread{
    int time;
    public void run() {
    System.out.println(getName()+" Start!");
    try {
    sleep(time);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(getName()+" End!");
    }
    }
    5. sleep 方法
    用于使程序进行适当的休眠
    • 唤醒机制,用于两个线程之间进行协调工作。
    • 一个线程可以调用另外一个线程的 other.interrupt() 打断线程的休眠
    • 被打断的线程 或 抛出 InterruptedException 异常
    在进入 sleep block 时候,线程不占用处理器。操作系统可以调度其他线程在处理器中执行。
    可以充分使用处理器资源


    案例
    public class Demo07 {
    public static void main(String[] args) {
    SleepThread t1 = new SleepThread();
    SleepThread t2 = new SleepThread();
    t1.time = 1000;
    t2.time = 2000;
    t1.other = t2;
    t1.start();
    t2.start();
    }
    }
    class SleepThread extends Thread{
    int time;
    SleepThread other;
    public void run() {
    System.out.println(getName()+" 开始");
    try {
    sleep(time);
    System.out.println(
    getName()+":大梦谁先醒!");
    // 在 t1 中叫醒 t2
    other.interrupt();
    } catch (InterruptedException e) {
    e.printStackTrace();
    System.out.println(
    getName()+":吵醒不高兴!");
    }
    System.out.println(getName()+" 结束");
    }
    }
    6. yield 方法
    yield 让出方法
    • 线程让出当前占用的处理器,返回 Runnable 状态


    案例:
    public class Demo08 {
    public static void main(String[] args) {
    Thread3 t1 = new Thread3();
    Thread3 t2 = new Thread3();
    t1.str = "A";
    t2.str = "B";
    t1.start();
    t2.start();
    }
    }
    class Thread3 extends Thread{
    String str;
    public void run() {
    for(int i=0; i<100; i++){
    System.out.println(
    getName() + "->" + str);
    /**
    * 在运行期间,让出当前线程正在占用的
    * 处理器资源。
    */
    yield();
    }
    }
    }
    7. join 方法
    join 连接两个线程
    • 一个线程等到另外一个线程结束再继续运行。
    • 等待时候线程进入 Block 状态
    • 等待期间如果被其他线程打断,就抛出中断异常。


    案例:一个线程产生 10 个数据,另外一个线程进行排序
    public class Demo09 {
    public static void main(String[] args) {
    T1 t1 = new T1();
    T2 t2 = new T2();
    List<Integer> list =
    new ArrayList<Integer>();
    t1.list = list;
    t2.list = list;
    t2.t1 = t1;
    t1.start();
    t2.start();
    }
    }
    class T1 extends Thread{
    List<Integer> list;
    public void run() {
    for(int i=0; i<10; i++){
    list.add((int)(Math.random()*100));
    }
    //
    System.out.println("T1:"+list);
    }
    }
    class T2 extends Thread{
    List<Integer> list;
    T1 t1;
    public void run() {
    try {
    t1.join();//
    //等到了 t1 线程正常结束
    Collections.sort(list);
    System.out.println("T2:"+list);
    } catch (InterruptedException e) {
    e.printStackTrace();
    // 发生了打断,结束了等待过程
    }
    }
    }
    四.线程同步
    1. 线程安全 API 与非线程安全 API
    ArrayList,LinkedList,HashSet 都不是线程安全的,
    Map 常用实现类 HashMap 也不是线程安全的。
    而线程安全的 List 是 Vector.
    线程安全的 Map 的实现类是 HashTable
    可以使用 Collections 提供的静态方法可以将当前的
    集合或 Map 转换为线程安全的。
    @author adminitartor
    案例:
    public class SyncApiDemo {
    public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("one");
    list.add("two");
    list.add("three");
    list.add("four");
    /*
    * 将给定的 List 集合转换为线程安全的集合。
    * 返回的集合中仍然包含所有元素。
    */
    list = Collections.synchronizedList(list);
    System.out.println(list);
    //HashSet 不是线程安全的
    Set<String> set = new HashSet<String>(list);
    //转换为线程安全的 Set 集合
    set = Collections.synchronizedSet(set);
    //HashMap 也不是线程安全的
    Map<String,Integer> map= new HashMap<String,Integer>();
    map.put("语文", 99);
    map.put("数学", 98);
    map.put("英语", 97);
    map = Collections.synchronizedMap(map);
    System.out.println(map);
    }
    }
    2. 线程池
    * 解决了两个问题:
    * 1:控制线程数量
    * 2:重用线程
    * 大量并发运行的线程会导致 CPU 过度切换,一起内存
    * 消耗,严重时可能导致系统崩溃。
    * 当需要频繁的创建线程,或线程数量过多时,应当考虑
    * 使用线程池来维护管理线程。
    案例:
    public class ThreadPoolDemo {
    public static void main(String[] args) {
    //创建固定大小线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(2);
    for(int i=0;i<5;i++){
    Runnable runnable = new Runnable(){
    public void run(){
    Thread t = Thread.currentThread();
    System.out.println(t+"正在执行一个任务!");
    try {
    Thread.sleep(5000);
    } catch (InterruptedException e) {
    System.out.println(t+"被中断了!");
    }
    System.out.println(t+"执行任务完毕!");
    }
    };
    System.out.println("指派任务"+i+"给线程池");
    threadPool.execute(runnable);
    }
    /*
    * shutdown 方法关闭线程池是在线程池中
    * 所有任务都被执行完毕后进行关闭的。
    * 而 shutdownNow 则是立即关闭线程池。
    */
    // threadPool.shutdown();
    threadPool.shutdownNow();
    System.out.println("关闭了线程池!");
    }
    }
    静态方法被 synchronized 修饰后,同步监视器
    * 对象为:当前类对象
    * 由于一个类只有一个类对象,所以该静态方法一定
    * 具备同步效果。
    * 类对象:Class 的实例,JVM 在加载一个类的时候会
    * 创建一个 Class 实例用于表示它。所以每个类只有
    * 且仅有一个 Class 的实例。静态方法就是将该对象
    * 上锁,所以静态方法若被 synchronized 修饰后一
    * 定具有同步效果。
    * @author adminitartor
    *
    */
    public class SyncDemo3 {
    public static void main(String[] args) {
    final Foo f1 = new Foo();
    final Foo f2 = new Foo();
    Thread t1 = new Thread(){
    public void run(){
    f1.dosome();
    }
    };
    Thread t2 = new Thread(){
    public void run(){
    f2.dosome();
    }
    };
    t1.start();
    t2.start();
    }
    }
    class Foo{
    public synchronized static void dosome(){
    Thread t = Thread.currentThread();
    System.out.println(t+"正在运行 dosome 方法");
    try {
    Thread.sleep(5000);
    } catch (InterruptedException e) {
    }
    System.out.println(t+"运行 dosome 方法完毕");
    }
    }

  • 相关阅读:
    java 编程思想——基础篇
    JDK编译器针对final变量的优化
    Java源码分析:深入探讨Iterator模式——迭代器模式
    JDK源码分析:java.lang.String
    JDK源码分析——Java.util.Vector的浅析
    JDK1.5下的String优化
    关于GDAL打开hfa大文件的问题
    使用SQLite3支持中文路径
    如何使用GDAL进行AOI裁剪
    常用直线生成算法
  • 原文地址:https://www.cnblogs.com/caoyingjielxq/p/9280585.html
Copyright © 2020-2023  润新知