Java基础 -- 线程

来源:互联网 发布:青岛java招聘 编辑:程序博客网 时间:2024/05/20 07:36

前言
线程是Java基础中的重要一章,必须要熟练掌握基本概念和使用方法。

目录

这里写图片描述

1. 基本概念

1.1 线程
操作系统中对线程的定义是程序执行的最小单元;实际上我们可以理解为一个进程里面的不同的执行路径。

1.2 进程
进程是一个静态的概念,可以理解为一个可执行的java文件。

2. 创建和启动
线程的创建和启动有两种方式:

(1)继承Thread类

public class ThreadStart {    public static void main(String[] args) {        Thread thread = new Thread1();        thread.start();    }}class Thread1 extends Thread {    @Override    public void run() {      }}

(2)实现Runnable接口

public class TestThread1 {    public static void main(String[] args) {        Thread thread1 = new Thread(new Runnable1());        thread1.start();        for (int i = 0; i < 100; i ++ ) {            System.out.println("main: " + i);        }    }}class Runnable1 implements Runnable {    @Override    public void run() {        for (int i = 0; i < 100; i ++ ) {            System.out.println("thread: " + i);        }    }}

(3)两种方法的对比
实现Runnable的接口要比继承Thread类要好,因为继承只能有一个父类,比较单一且不好控制。

3. 调度和优先级

线程的优先级基础的分为1-10级,默认值为5,优先级越高,获得的CPU的执行时间片越长:

Thread thread1 = new Thread(new Runnable1());thread1.setPriority(Thread.MAX_PRIORITY);thread1.start();

4. 状态控制

4.1 线程状态

这里写图片描述

4.2 执行方法

(1)sleep()
该方法是Thread类的静态方法,使线程处于睡眠等待的状态,让出cpu的执行权。

(2)interrupt()
需要捕获InterruptException的异常,当调用线程的该方法时,停止线程的执行,但是会先执行完InterruptException的异常处理之后,再结束线程。

(3)stop()
停止线程,该方法已经废弃,一旦调用会立即结束线程。

(4)join()
合并两个线程的执行。当在线程A中执行调用线程B的join方法时,线程B合并到A中执行,B结束后再接着执行线程A。

(5)yield()
当调用线程的该方法时,该线程立刻释放CPU的占用,重新进入就绪等待的状态。

(6)isAlive()
判断当前线程是否还存活:

Thread.currentThread().isAlive();

4.3 结束线程的方法
对比:
a. stop方法比interrupt粗暴,直接结束线程;
b. 结束一个线程的最好方法是,结束run()方法的执行。run()方法结束,那么线程也就结束了。

5. 线程同步

线程同步可以直接理解为资源独占,也就是同一时期的某个资源只能被一个线程独占。

5.1 互斥锁

资源独占可以用synchronized关键字进行修饰。
synchronized

a. 修饰方法:当synchronized修饰一个方法时,该线程拥有该对象的锁(Object本身就是一个对象锁),则其他线程会阻塞 无法访问该方法,直到独占的线程释放该对象锁。
这种情况下,其他线程可以访问该对象的其他方法。

    public synchronized void printDataA() {        try {            System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName());            Thread.sleep(5000);            flag++;            System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName());        } catch (InterruptedException e) {        }        System.out.println("Thread : " + Thread.currentThread().getName() + " , flag = " + flag);    }

b. 修饰代码块:使用关键字时需要指明具体的锁

public void printDataB() {        flag++;        System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName());         synchronized (this) {             try {                 flag++;                 System.out.println("Thread : " + Thread.currentThread().getName() + " , flag = " + flag);                 //System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName());                 Thread.sleep(1000);                 System.out.println("Current: " + new Date() + " === " + Thread.currentThread().getName());             } catch (InterruptedException e) {             }         }    }

5.2 死锁

死锁是指两个线程或多个线程之间相互等待需要的资源对象的现象,由于线程之间的相互等待,导致线程永远处于阻塞状态无法继续执行。经典的问题如哲学家吃饭。

针对死锁的问题,可以通过添加同步锁的方式(资源独占)进行解决。但是如果要保护好被访问的对象,需要仔细考虑所有操作该对象的方法是否被加了同步锁。

5.3 生产者/消费者 问题

生产者/消费者模型,是典型的容易发生死锁现象的问题。

5.3.1 wait和notify

该模型的具体代码见下面,这里使用到了线程控制的另一种方法wait(),该方法是Object对象的方法,与其他线程控制的方法不同。
wait():当调用某个对象的wait()方法时,说明锁定在该对象上的线程需要等待,让出cpu执行权。

当调用wait方法时,该线程处于睡眠状态,那么如何唤醒该线程呢?

notify()/notifyAll():叫醒wait在当前对象上的线程。如果某个线程处于wait状态时,Object不发通知也会产生死锁的现象。

5.3.2 wait和sleep的区别

wait属于Object的方法,sleep是Thread类的方法;
wait时会释放线程所占用的锁,sleep不会释放线程占用的锁;

5.3.3 生产者/消费者模型:

public class ProducerCunsumer {    public static void main(String[] args) {        Basket basket = new Basket();        new Thread(new Producer(basket)).start();        new Thread(new Consumer(basket)).start();    }}class Wotou {    public Wotou(){}}class Basket{    int index = 0;    int length = 5;    Wotou[] array = new Wotou[length];    public synchronized void push(Wotou m) {        while (index == length) {            try{                wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        notify();        System.out.println("set wotou: " + index);        array[index] = m;        index++;    }    public Wotou pop() {         try {            Thread.sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        synchronized(this) {            while (index == 0) {                try {                    wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            notify();            index--;            Wotou result = array[index];            System.out.println("get wotou: " + index);            return result;        }    }}class Producer implements Runnable {    Basket basket;    Producer(Basket basket) {        this.basket = basket;    }    @Override    public void run() {        for (int i = 0; i < 20; i++) {            basket.push(new Wotou());        }    }}class Consumer implements Runnable {    Basket basket;    Consumer(Basket basket) {        this.basket = basket;    }    @Override    public void run() {        for (int i = 0; i < 20; i++) {            basket.pop();        }    }}

6. 总结
线程的基本概念和使用方法基本上已经介绍清楚,相关的测试代码会上传到github上。

原创粉丝点击