多线程

一、多线程

      1.并发与并行

  • 并发:指两个或多个事件在同一个时间段内发生。

  • 并行:指两个或多个事件在同一时刻发生(同时发生)。

     2.线程和进程

  • 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

  • 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

     3.1.创建线程一

                       1)定义Thread类的子类(继承Thread类),并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行。

                       2)创建Thread子类的实例,即创建了线程对象

                       3)调用线程对象的start()方法来启动该线程

例:

public class MyThread extends Thread {

//定义指定线程名称的构造方法
    public MyThread(String name) {
        //调用父类的String参数的构造方法,指定线程的名称
        super(name);
    }
    /**
     * 重写run方法,完成该线程执行的逻辑
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+":正在执行!"+i);
        }
    }
    public static void main(String[] args) {
        //创建自定义线程对象
        MyThread mt = new MyThread("新的线程!");
        //开启新线程
        mt.start();
        //在主方法中执行for循环
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程!"+i);
        }
    }
}

        3.1.1.Thread类的构造方法

           public Thread() :分配一个新的线程对象。

           public Thread(String name) :分配一个指定名字的新的线程对象。

           public Thread(Runnable target) :分配一个带有指定目标新的线程对象。

          public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字

       3.1.2.常用方法

       public String getName() :获取当前线程名称。

  public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。

  public void run() :此线程要执行的任务在此处定义代码。

  public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

  public static Thread currentThread() :返回对当前正在执行的线程对象的引用。 

      3.2.创建线程二

          1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

   2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正 的线程对象。

   3. 调用线程对象的start()方法来启动线程

例:

             

 

          3.3.Thread和Runnable的区别 

        如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享;

  实现Runnable接口比继承Thread类所具有的优势:

    1. 适合多个相同的程序代码的线程去共享同一个资源。

    2. 可以避免java中的单继承的局限性。

    3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

    4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

     4.线程的同步

       4.1.同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

          

 

例:

 

 

        同步锁:

             1. 锁对象 可以是任意类型。

             2. 多个线程对象 要使用同一把锁。
           注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着

        4.2.同步方法

  •  同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外 等着。

格式:

 

         同步锁:

                   对于非static方法,同步锁就是this。
                   对于static方法,我们使用当前方法所在类的字节码对象(类名.class)

          4.3.Lock锁

  • Lock比synchronized代码块和synchronized方法更广泛的锁定操作, 同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。 

            1) public void lock() :加同步锁。

            2) public void unlock() :释放同步锁

            3)Lock接口的实现类ReentrantLock

例:

         

 

       5.线程状态

              

 

 二、线程池

       1.等待唤醒机制

  • 等待唤醒机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:
  1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中

  2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先入座。

  3. notifyAll:则释放所通知对象的 wait set 上的全部线程。

  •  调用wait和notify方法需要注意的细节
  1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。

  2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。

  3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。

    2.线程池

 

 线程池优点:   

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

三、Lambda表达式

          借助Java 8的全新语法,上述Runnable接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效:

public class Demo02LambdaRunnable {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("多线程任务执行!")).start(); // 启动线程
    }
}

 

posted @ 2020-03-05 15:37  撑起一片阳光  阅读(751)  评论(0编辑  收藏  举报