zoukankan      html  css  js  c++  java
  • Java多线程

    多线程

    当今的操作系统绝大部分都是基于 多任务 的操作系统;多任务操作系统的最大特点,是可以同时 运行多个程序;由于操作系统支持 时间片 轮换算法,使得用户感觉多个程序在同时运行,似乎有多个CPU在起作用。

    运行在操作系统之上的每个应用程序,都会占用一个独立的 进程(process),而 进程内又允许运行多个线程(thread),这意味着一个程序可以同时执行多个任务的功能;在基于线程的多任务而处理环境中, 线程是执行特定任务的可执行代码的最小单位;多线程帮助你写出CPU最大利用率的高效程序,因为空闲时间保持最低,这对Java运行的交互式的网络互连环境是至关重要的,例如:网络的数据传输速率远低于计算机的处理能力,在传统的单线程环境中,你的计算机必须花费大量的空闲时间来等待,多线程能够使你充分利用这些空闲时间。

    进程和线程的区别

    进程是指系统中正在运行中的应用程序,它拥有自己独立的内存空间;

    线程是指进程中一个执行流程,一个进程中允许同时启动多个线程,他们分别执行不同的任务;

    线程与进程的主要区别在于:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中,这些线程可以共享数据,因此线程间的通信比较简单,消耗的系统开销也相对较小。

    在Java中实现线程有两种方式

      java.lang.Thread

      java.lang.Runnable

    主线程

    任何一个Java程序启动时,一个线程立刻运行,它执行 main 方法,这个线程称为程序的 主线程; 也就是说,任何Java程序都 至少有一个线程 ,即主线程; 主线程的特殊之处在于:

      它是产生其它线程子线程的线程;

      通常它必须最后结束,因为它要执行其它子线程的关闭工作。

    public class MainThreadDemo {
    public static void main(String[] args) {
      Thread tMain=Thread.currentThread();
      System.out.println("当前的线程是:"+tMain);
    try{
      for(int i=0;i<5;i++){
      System.out.println(i);
      Thread.sleep(2000);
    }
    }catch (Exception e) {
      e.printStackTrace();
    }
    }
    }

    只有Thread.start()才能创建线程,执行线程中的run方法。 如果直接调用run方法,则无法创建线程。

    Thread类示例

    泡茶的时候可以一边洗杯子,一边烧水。

    public class MakeTea extends Thread {
    public static void main(String[] args) {
      new BoilThread().start();
      new WashThread().start();
    }
    }

    class BoilThread extends Thread {
    @Override
    public void run() {
    try {
      System.out.println("开始烧水");
      Thread.sleep(10000);
      System.out.println("水烧开了");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    }
    }

    class WashThread extends Thread {
    @Override
    public void run() {
    try {
      for (int i = 1; i <= 5; i++) {
      System.out.println("开始洗第" + i + "个茶杯");
      Thread.sleep(1500);
      System.out.println("第" + i + "个茶杯洗干净了");
    }
    } catch (Exception e) {
      e.printStackTrace();
    }
    }
    }

    Runnable接口

    public class MakeTeaRunnable {
    public static void main(String[] args) {
      new Thread(new BoilThreadRunnable()).start();
      new Thread(new WashThreadRunnable()).start();
    }
    }

    class BoilThreadRunnable implements Runnable {

    @Override
    public void run() {
    try {
      System.out.println("开始烧水");
      Thread.sleep(10000);
      System.out.println("水烧开了");
    } catch (Exception e) {
      e.printStackTrace();
    }
    }
    }

    class WashThreadRunnable implements Runnable {

    @Override
    public void run() {
    try {
      for (int i = 1; i < 5; i++) {
      System.out.println("开始烧" + i + "杯水");
      Thread.sleep(1500);
      System.out.println("第" + i + "个被子洗干净了");
    }
    } catch (Exception e) {
      e.printStackTrace();
    }

    }

    }

    线程同步

    多线程共享内存时,如果其中有一个线程对数据进行修改,那么可能出现数据不一致的情况,这时就需要使用同步来解决这个问题。 需要同步的场景:

      至少有一个共享数据

      至少有一个线程对数据进行修改

    同步锁的实现

      同步块:(同步块内容一定不能太多,影响运行效率)

    只有 引用类型 才能加同步锁。(基础类型的 包装类 也不能加同步锁,因为装箱类型和字符串的值都具有不可变性。意思是:改变值就会使内存地址跟着改变。内存地址改变会使同步锁就会失去共享对象,从而失效。)

    多线程的通讯

    通过对共享数据进行 wait() 和 notify() ,来达到多线程之间的相互牵制。

      wait():让本线程进入等待状态,同时释放同步锁。

      notify():通知等待状态的线程,继续执行。

      notifyAll():通知所有等待状态的线程,继续执行。

    wait和sleep的区别 :·

    1. wait会释放同步锁,sleep不会。在我们生产者和消费者案例里头,如果使用sleep,不管生产者是否生产出商品,消费者都要睡指定长的时间。但如果使用wait,只要生产者生产出商品就可以立即执行。
    2. wait是Object的方法,sleep是Thread的静态方法。

    计算机只有一个CPU,各个线程 轮流获得CPU 的使用权,才能执行任务;优先级较  的线程有 更多 获得CPU的机会,反之亦然;优先级用 整数 表示,取值范围是 1~10 ,一般情况下,线程的默认优先级都是 5 ,但是也可以通过 setPriority 和 getPriority 方法来设置或返回优先级;

    多线程死锁

    两个线程各自等待对方的资源,并且不释放对方想要的资源。

    线程等待

    两个线程,一个线程需要等待另一个线程执行后再执行。

    线程状态

    新建状态:使用new关键字创建线程对象,仅仅被分配了内存;

    就绪状态:线程对象被创建后,等待它的start方法被调用,以获得CPU的使用权;

    运行状态:执行run方法,此时的线程的对象正占用CPU;

    睡眠状态:调用sleep方法,线程被暂停,睡眠时间结束后,线程回到就绪状态,睡眠状态的线程不占用CPU;

    死亡状态:run方法执行完毕后,线程进入死亡状态;

    阻塞状态:线程由于某些事件(如等待键盘输入)放弃CPU,暂停运行,直到线程重新进入就绪状态,才有机会转到运行状态;

    sleep、yield和join

    sleep和yield都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,但两者的区别在于:

      sleep给其它线程运行的机会,但不考虑其它线程的优先级;但yield只会让位给相同或更高优先级的线程;

      当线程执行了sleep方法后,将转到阻塞状态,而执行了yield方法之后,则转到就绪状态;

    sleep方法有可能抛出异常,而yield则没有;

      在一般情况下,我们更建议使用sleep方法。

    join方法用于等待其它线程结束,当前运行的线程可以调用另一线程的join方法,当前运行线程将转到阻塞状态,直至另一线程执行结束,它才会恢复运行。

  • 相关阅读:
    2018.9.22 Bubble Cup 11-Finals(Online Mirror,Div.2)
    2018.9.21 Codeforces Round #511(Div.2)
    2018.9.20 Educational Codeforces Round 51
    解题:USACO12OPEN Bookshelf
    解题:CF983B pyramid
    1214. 许可证密钥格式
    1212. 最大连续1的个数
    1270. 勒索信
    1250. 第三大的数
    1218. 补数
  • 原文地址:https://www.cnblogs.com/qhcyp/p/10651726.html
Copyright © 2011-2022 走看看