编程语言
首页 > 编程语言> > 【JavaSE】---多线程

【JavaSE】---多线程

作者:互联网

【JavaSE】---多线程

前言:并发执行与并行执行

一、线程的基本概念

1、线程的概念

多线程是指在同一个进程中同时存在几个执行体,按几条不同的执行路径同时工作的情况。

2、程序、进程、多任务与线程

3、线程的状态与生命周期

image-20211007094101816

4、线程的优先级与调度

①优先级

②调度

二、Java的Thread线程类与Runnable

​ Java实现多线程的方法有两种:继承java.lang包下面的Thread类,另一种就是用户在定义自己的类中实现Runnable接口。但是不管采用什么方法,都要用到Java语言类库中的Thread类以及相关的方法。

1、利用Thread类的子类来创建线程

①Thread类的构造方法

构造方法 功能说明
public Thread() 创建一个线程对象,此线程对象的名称是“Thread-n"的形式,n为整数,使用这个构造方法,必须创建Thread类的一个子类并覆盖其run()方法。
public Thread(String name) 创建一个线程对象,参数name指定了线程的名称。
public Thread(Runnable target) 创建一个线程对象,此线程对象的名称是”Thread-n"的形式,其中n是一个整数。参数target的run()方法将被线程对象调用,作为其执行代码。
public Thread(Runnable target,String name) 功能同上,name指定了新创建线程的名称。

②Thread类的常用方法

常用方法 功能说明
public static Thread currentThread() 返回当前正在执行的线程对象
public final String getName() 返回线程的名称
public void start() 使该线程由新建状态变为就绪状态,如果该线程已经是就绪状态,则产生IllegalStateException异常
public void run() 线程应该执行的任务
public final boolean isAlive() 如果线程处于就绪、阻塞或者运行状态,返回true,否则返回false
public void interrupt() 当线程处于就绪或者执行状态时候,给该线程设置中断标志。一个正在执行的线程让睡眠线程调用这个方法,则可导致睡眠线程发生InterruptedException异常而唤醒自己,从而进入就绪状态。
public static boolean isInterrupted() 判断该线程是否被中断,若是返回true,否则返回false
public final void join() 暂停当前线程的执行,等待调用该方法的线程结束后再继续执行本线程
public final int getPriority() 返回线程的优先级
public final void setPriority(int newPriority) 设置线程的优先级。如果当前线程不能修改这个线程,则产生SecurityException异常。
public static void sleep(long milis) 指定当前线程的睡眠时间。参数millis是线程睡眠的毫秒数,如果这个线程已经被别的线程中断,则产生InterruptedException异常
public static void yield() 暂停当前线程的执行,但该线程仍处于就绪状态,不转为阻塞状态。该方法只给同优先级线程以执行的机会

要在一个Thread的子类里激活线程,必须做好下面两件事情:

③测试案例

package thr01;

public class MyThread extends Thread{
    private String who;
    public MyThread(String str){
        who = str;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                sleep((int)(1000 * Math.random()));
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(who + "正在运行!");
        }
    }
}

class App11_1{
    public static void main(String[] args) {
        MyThread you = new MyThread("你");
        MyThread she = new MyThread("她");
        you.start();
        she.start();
        System.out.println("主线程main()运行结束");
    }
}

需要注意的点就是上面sleep()函数,Math.random()返回的时0~1的浮点数。

2、用Runable接口来创建线程

需求:

测试案例

package runable01;

public class MyThread implements Runnable{
    private String who;
    public MyThread(String str){
        who = str;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep((int)(1000 * Math.random()));
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(who + "正在运行!");
        }
    }
}

class App11_2{
    public static void main(String[] args) {
        MyThread you = new MyThread("你");
        MyThread she = new MyThread("她");
        Thread t1 = new Thread(you);
        Thread t2 = new Thread(she);
        t1.start();
        t2.start();
    }
}

3、join()方法的使用

​ 从前面的例子中,我们看到,程序中被同时激活的多个线程将会同时执行,但是有时候需要有序地执行,这个时候可以使用Thread类中的join()方法。当某一个线程调用join()方法的时候,则其他线程会等到该线程结束后才开始执行。也就是说t.join()将使得t线程“加塞”到当前线程之前获得CPU,当前线程则进入阻塞状态,指导线程t结束为止,当前线程恢复为就绪状态,等待调度。

测试案例

package join01;
class MyThread extends Thread{
    private String who;
    public MyThread(String str){
        who = str;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                sleep((int)(1000 * Math.random()));
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(who + "正在运行!");
        }
    }
}
public class App11_3 {
    public static void main(String[] args) {
        thr01.MyThread you = new thr01.MyThread("你");
        thr01.MyThread she = new thr01.MyThread("她");
        you.start();
        try {
            you.join();
        }
        catch (InterruptedException e){}
        she.start();
        try {
            she.join();
        }
        catch (InterruptedException e){}
        System.out.println("主方法main()运行结束");
    }
}

激活线程you之后,继续往下执行,但是下面有you.join()语句,所以它会使得程序的流程先停留在此处,直到you线程结束之后,才会执行到第14行的she线程。同理,由于she线程也调用了join()方法,所以要等到she线程结束之后,才会输入主方法结束。

4、线程之间的数据共享

①测试案例-01

package ticket;

class ThreadSale extends Thread{
    private int tickets = 10;

    @Override
    public void run() {
        while (true){
            if (tickets > 0)
                System.out.println(this.getName() + " 售机票第"+tickets--+"号");
            else
                System.exit(0);
        }
    }
}
public class App11_4 {
    public static void main(String[] args) {
        ThreadSale t1 = new ThreadSale();
        ThreadSale t2 = new ThreadSale();
        ThreadSale t3 = new ThreadSale();
        t1.start();
        t2.start();
        t3.start();
    }
}

结果:

Thread-0 售机票第10号
Thread-0 售机票第9号
Thread-0 售机票第8号
Thread-0 售机票第7号
Thread-0 售机票第6号
Thread-0 售机票第5号
Thread-0 售机票第4号
Thread-0 售机票第3号
Thread-0 售机票第2号
Thread-2 售机票第10号
Thread-2 售机票第9号
Thread-2 售机票第8号
Thread-2 售机票第7号
Thread-1 售机票第10号
Thread-2 售机票第6号
Thread-0 售机票第1号
Thread-2 售机票第5号
Thread-2 售机票第4号
Thread-2 售机票第3号
Thread-2 售机票第2号
Thread-2 售机票第1号
Thread-1 售机票第9号
Thread-1 售机票第8号
Thread-1 售机票第7号
Thread-1 售机票第6号
Thread-1 售机票第5号
Thread-1 售机票第4号
Thread-1 售机票第3号
Thread-1 售机票第2号
Thread-1 售机票第1号

分析:

②测试案例-02

package ticket;

class ThreadSale2 implements Runnable{
    private int tickets = 10;

    @Override
    public void run() {
        while (true){
            if (tickets >0)
                System.out.println(Thread.currentThread().getName()+"售机票第"+tickets--+"号");
            else
                System.exit(0);
        }
    }
}

public class App11_5 {
    public static void main(String[] args) {
        ThreadSale2 t = new ThreadSale2();
        Thread t1 = new Thread(t, "第1售票窗口");
        Thread t2 = new Thread(t, "第2售票窗口");
        Thread t3 = new Thread(t, "第3售票窗口");
        t1.start();
        t2.start();
        t3.start();
    }
}

结果:

第1售票窗口售机票第10号
第1售票窗口售机票第7号
第1售票窗口售机票第6号
第1售票窗口售机票第5号
第1售票窗口售机票第4号
第1售票窗口售机票第3号
第1售票窗口售机票第2号
第1售票窗口售机票第1号
第3售票窗口售机票第8号
第2售票窗口售机票第9号

结论:

三、多线程的同步控制

1、用户从银行取款出错案例

package bank01;

class Mbank{
    private static int sum = 2000;
    public static void take(int k){
        int temp = sum;
        temp -= k;
        try{
            Thread.sleep((int)(1000 * Math.random()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        sum = temp;
        System.out.println("sum=" + sum);
    }
}
class Customer extends Thread{
    public void run(){
        for (int i = 1;i<=4;i++)
            Mbank.take(100);
    }
}
public class App11_6 {
    public static void main(String[] args) {
        Customer c1 = new Customer();
        Customer c2 = new Customer();
        c1.start();
        c2.start();
    }
}

结果:

sum=1900
sum=1900
sum=1800
sum=1800
sum=1700
sum=1700
sum=1600
sum=1600

错误原因:

2、互斥锁

image-20211007160723707

synchronized的用法:

//格式一:同步语句
Synchronized(对象){
    //临界代码段
}

//格式二:
public synchronized 返回类型 方法名(){
    //方法体
}
//或者
public 返回类型 方法名(){
    synchronized(this){
        //方法体
    }
}
package bank02;
class Mbank{
    private static int sum = 2000;
    public synchronized static void take(int k){
        int temp = sum;
        temp -= k;
        try{
            Thread.sleep((int)(1000 * Math.random()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        sum = temp;
        System.out.println("sum=" + sum);
    }
}
class Customer extends Thread{
    public void run(){
        for (int i = 1;i<=4;i++)
            Mbank.take(100);
    }
}
public class App11_7 {
    public static void main(String[] args) {
        Customer c1 = new Customer();
        Customer c2 = new Customer();
        c1.start();
        c2.start();
    }
}

结果:

sum=1900
sum=1800
sum=1700
sum=1600
sum=1500
sum=1400
sum=1300
sum=1200

3、synchronized的说明

image-20211007162123262

四、线程之间的通信

image-20211007163019764

image-20211007163032605

1、模拟存票、售票的过程

要求每存入一张票,就售出一张票,售出后,再存入,直到售完为止。

package ticket02;

public class App11_8 {
    public static void main(String[] args) {
        Tickets t = new Tickets(10);
        new Producer(t).start();
        new Consumer(t).start();
    }
}

class Tickets{
    protected int size;//总票数
    int number = 0;//票号
    boolean available = false;//表示当前是否有票可售
    public Tickets(int size){
        this.size = size;
    }
    public synchronized void put(){//同步方法,实现存票功能
        if (available){//如果还有存票待售出,则存票线程等待
            try {
                wait();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        System.out.println("存入第【"+(++number)+"】号票");
        available = true;
        notify();//存入票后唤醒售票线程开始售票
    }
    public synchronized void sell(){//同步方法,实现售票功能
        if (!available){//如果没有存票,则售票线程等待
            try {
                wait();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        System.out.println("售出第【"+(number)+"】号票");
        available = false;
        notify();//售票后唤醒存票线程开始存票
        if (number == size)
            number = size + 1;//在售完最后一张票后,设置一个结束标志
        //number > size表示售票结束
    }
}

class Producer extends Thread//存票线程类
{
    Tickets t = null;
    public Producer(Tickets t){
        this.t = t;
    }
    public void run(){
        while (t.number < t.size){
            t.put();
        }
    }
}

class Consumer extends Thread //售票线程类
{
    Tickets t = null;
    public Consumer(Tickets t){
        this.t = t;
    }
    public void run(){
        while (t.number <= t.size)
            t.sell();
    }
}

image-20211007164658079

标签:机票,Thread,void,---,线程,sum,JavaSE,多线程,public
来源: https://www.cnblogs.com/darkerg/p/15376378.html