线程

一.程序、进程和线程

1.程序

        程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一 段静态的代码。

2.进程

        进程((process)正在内存中运行的应用程序,如运行中的QQ,运行中的音乐播 放器。进程是操作系统进行资源分配的最小单位。

3.线程

        线程(thread)进程可进一步细化为线程,是一个进程内部的最小执行单元,是操 作系统进行任务调度的最小单元,隶属于进程。

注:

  • 一个进程可以包含多个线程。
  • 一个线程只能属于一个进程,线程不能脱离进程而独立运行。
  • 每个进程至少包含一个线程(称为主线程)。
  • 在主线程中可以创建并启动其他的线程。
  • 一个进程内的所有线程共享该线程的内存资源。

二.线程的创建 

 1.继承Thread类的方式

在Java中要实现线程,最简单的方式就是继承Thread类,并重写其中的run方法,方法原型如下:

        ①创建一个类继承Thread类

public class MyThread extends Thread{

}

        ②在该类中重写Thread类中的run方法

public class MyThread extends Thread{

    // 重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("MyThread:" + i);
        }
    }
}

        ③在主线程中创建Thread对象,并调用start方法。

                注意:启动线程是调用Thread中的start方法,不是调用run;调用run方法只是方法的调用,并不是启动线程

public class Test {
    public static void main(String[] args) {
        // 创建线程对象
        MyThread myThread = new MyThread();
        // 启动线程要调用Thread中的start方法
        myThread.start();
    }
}

2. 实现Runnable接口的方式

java.lang.Runnable接口中仅仅只有一个抽象方法,Runnable接口的存在主要是为了解决Java中不允许多重继承的问题。

public void run() 

方法原型如下: 

       ①创建一个类实现Runnable接口,并重写run方法

public class MyTask  implements Runnable{
    /*
        public abstract void run();
     */
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("myTask:" + i);
        }
    }
}

        ②在主线程中创建该类的对象

public class Test {
    /*
        方法二:
            实现Runnable接口,重写run方法
            在主线程中创建继承类的对象,
            再创建Thread对象中的构造方法,调用start方法
     */
    public static void main(String[] args) {
        // 创建类对象
        MyTask myTask = new MyTask();
        }
    }
}

③再创建Thread对象中的构造方法,调用start方法

public class Test {
    public static void main(String[] args) {
        // 创建类对象
        MyTask myTask = new MyTask();
        // 创建线程对象,调用构造方法
        Thread thread = new Thread(myTask); //多态
        // 调用Thread中的start方法
        thread.start(); //private native void start0();本地方法
    }
}

 3.实现Runnable接口的好处

        ①避免了java中单继承的局限性

        ②多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

三.Thread类中的方法

方法原型 说明
void start() 启动线程
final String getName() 返回线程的名称
final void setPriority(int newPriority) 设置线程的优先级
final int getPriority() 返回获取线程的优先级
final void join() 等待线程终止
static Thread currentThread() 返回对当前正在执行的线程对象的引用
static void sleep(long millis) 让当前正在执行的线程休眠(暂停执行), 休眠时间由milli s(毫秒)指定
String getName() 获取线程的名字
String setName() 为线程设置名字

四.线程的优先级

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

        ① MAX_PRIORITY:取值为10,表示最高优先级。

        ② MIN_PRIORITY:取值为1,表示最底优先级。

        ③ NORM_PRIORITY:取值为5,表示默认的优先级。

五.线程状态

线程_第1张图片

1.线程状态: 

  • 新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。
  • 就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是还没有分配到CPU资源。
  • 运行:当已经就绪的线程被调度并获得CPU资源时,便进入运行状态,用run()方法定义了线程的操作和功能。
  • 阻塞: 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态。
  • 死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束。

 2.守护线程

线程_第2张图片

方法   void  setDamon(boolean on)  设置线程为守护线程(true)或用户线程(false)

        必须在线程启动前设置

六.多线程 

1.多线程的概念

        多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。通俗来讲,就是在一个程序内部创建多个和线程执行任务。

2.什么时候使用多线程?

        ① 程序需要同时执行两个或多个任务。

        ② 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、 网络操作、搜索等。

3.多线程的优点

        ①同时执行多个任务,提高了代码的运行效率。

        ②多线程程序中,多个线程共享计算机的资源,提高了系统的资源利用率。

        ③多线程程序中,各个线程之间相互独立,易于维护和扩展。

        ④改善程序结构,将复杂任务分为多个线程,独立运行。

4.多线程的缺点

        ①线程也是程序,线程数过多,会影响程序的性能,消耗系统资源。

        ②多个线程同时访问共享资源,容易出现线程安全问题。

        ③多线程需要协调和管理,所以需要跟踪管理线程,使得cpu开销变大。

七.线程同步

1.为什么采用线程同步

        多个线程同时读写同一份共享资源时,可能会引起冲突。所以引入线同步机制, 即各线程间要有先来后到。

2.什么是同步

        同步就是排队+锁:

                几个线程之间要排队,一个个对共享资源进行操作,而不是同时进行操作;为了保证数据在方法中被访问时的正确性,在访问时加入锁机制。

3.解决方案

(1)使用synchronized关键字

        synchronized关键字,修饰代码块和方法。

修饰代码块:synchronized(同步对象/同步锁){

                                 // 多个线程对应的同步对象(同步锁)必须是同一个,用来记录有没有线程进入到同步代码块中。同步对象可以是 java中的任何类

                  }

修饰方法: synchronized修饰方法,同步对象会有默认的, 若修饰的方法是非静态方法,默认同步对象是this;若修饰的对象是静态方法,同步对象是当前类的class对象

package com.gty.thread.threadDemo5;

public class PrintNumTask extends Thread {
    static int num = 1;
    static Object object = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                // 唤醒
                object.notify();
                if (num <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num++;
                } else {
                    break;
                }
                try {
                    // 等待
                    object.wait();
                    Thread.sleep(400);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


package com.gty.thread.threadDemo5;

public class Test {
    public static void main(String[] args) {

        PrintNumTask printNumTask1 = new PrintNumTask();
        PrintNumTask printNumTask2 = new PrintNumTask();
        printNumTask1.setName("线程1");
        printNumTask2.setName("线程2");
        printNumTask1.start();
        printNumTask2.start();
    }
}

(2)Lock(锁),ReentratLock

  • 从JDK 5.0开始,Java提供了更强大的线程同步机制-通过显式定义同步锁对象 来实现同步。同步锁使用Lock对象充当。
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存 语义,在实现线程安全的控制中,可以显式加锁释放锁

   Lock  lock = new ReentratLock;

 ReentratLock实现了Lock接口,所以可以称为Lock锁。

与synchronized相比:

        ①ReentratLock是一种java代码层的控制实况

        ②而ynchronized是关键字,依靠底层编译后的指令实现;而ReentrantLock是一个类。

        ③synchronized用于修饰代码块和方法,ReentrantLock只能修饰代码块。

        ④synchronized属于隐式的加锁和释放锁,而ReentrantLock要通过手动加锁和释放锁,释放锁最好在 finally代码块中运行。

八.线程通信

1.概念

        线程通讯指的是多个线程通过相互牵制,相互调度,即线程间的相互作用。

2.三个方法

  • wait一旦执行此方法,当前线程就进入阻塞状态,并释放同步锁对象。
  • notify一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait, 就唤醒优先级较高的那个。
  • notifyAll一旦执行此方法,就会唤醒所有被wait的线程。 

注意:wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。 

3.wait()和sleep()区别

wait():

        ①线程等待,需要被唤醒;

        ②会释放锁;

        ③只能在同步代码块中使用。 

sleep():

        ①线程睡眠,不需要被唤醒,睡眠时间结束会自动启动;

        ②不会释放锁;

        ③可以在任何地方使用。

 

         

你可能感兴趣的:(java,jvm,开发语言)