Java多线程编程

1. Java多线程编程

在java中,如果要实现多线程,就必须依靠线程主体类,而java.lang.Thread是java中负责多线程操作类,只需继承Thread类,就能成为线程主体类,为满足一些特殊要求,也可以通过实现Runnable接口或者Callable接口来完成定义。

具体方式如下:

1.继承Thread类,重写run方法(无返回值)

2.实现Runnable接口,重写run方法(无返回值)

3.实现Callable接口,重写call方法(有返回值且可以抛出异常)

1)Thread类实现多线程
通过继承Thread类,并重写父类的run()方法实现
public void run()
参考代码:

package day03;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread t1 = new MyThread("线程a");
        MyThread t2 = new MyThread("线程b");
        MyThread t3 = new MyThread("线程c");
        MyThread t4 = new MyThread("线程d");
        MyThread t5 = new MyThread("线程e");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

class MyThread extends Thread {
    private String name;
    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.name + "正在工作中" + i);
        }
    }
}


输出:
线程b正在工作中0
线程e正在工作中0
线程c正在工作中0
线程a正在工作中0
线程d正在工作中0
线程a正在工作中1
线程c正在工作中1
线程e正在工作中1
线程b正在工作中1
线程e正在工作中2
线程b正在工作中2
线程c正在工作中2
线程a正在工作中2
线程d正在工作中1
线程a正在工作中3
线程c正在工作中3
线程b正在工作中3
线程e正在工作中3
线程b正在工作中4
线程c正在工作中4
线程a正在工作中4
线程d正在工作中2
线程d正在工作中3
线程d正在工作中4
线程a正在工作中5
线程c正在工作中5

2) Runnable接口实现多线程
使用Thread类的确可以实现多线程,但是也容易发现它的缺陷:面向对象的单继承局限,因此才采用Runnable接口来实现多线程。

该接口的定义如下(以下并不需要在代码中实现):

public interface Runnable {
     public void run();
}

参考代码:

package day06;

public class TestThread {
    public static void main(String[] args) {
        Runnable t1 = new MyThread("线程a");
        Runnable t2 = new MyThread("线程b");
        Runnable t3 = new MyThread("线程c");
        Runnable t4 = new MyThread("线程d");
        Runnable t5 = new MyThread("线程e");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
        new Thread(t4).start();
        new Thread(t5).start();
    }
}
class MyThread implements Runnable {
    private String name;
    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++)
        {
            System.out.println(this.name + " 正在执行中……" + i);
        }
    }
}


输出:
线程a 正在执行中……0
线程e 正在执行中……0
线程b 正在执行中……0
线程d 正在执行中……0
线程c 正在执行中……0
线程d 正在执行中……1
线程b 正在执行中……1
线程e 正在执行中……1
线程a 正在执行中……1
线程e 正在执行中……2
线程b 正在执行中……2
线程d 正在执行中……2
线程c 正在执行中……1
线程d 正在执行中……3
线程b 正在执行中……3
线程e 正在执行中……3
线程a 正在执行中……2
线程e 正在执行中……4
线程b 正在执行中……4
线程d 正在执行中……4
线程c 正在执行中……2
线程d 正在执行中……5
线程b 正在执行中……5
线程e 正在执行中……5
线程b 正在执行中……6

以上程序实例化伍个继承Runnable接口的MyThread类,然后通过Thread类的一个构造函数public Thread(Runnable target)分别实例化Thread类,再利用start()方法实现多线程。

Thread方案实现和Runnable方案实现的区别:

// Thread类定义:
public class Thread extends Object implements Runnable {}

从定义上看,Thread类是Runable接口的子类,通过直接覆写Thread类的run方法实际上依然是覆写Runnable接口内的run方法,其实本质上是没有区别的,但是利用Runnable方案实现更加能体现面向对象思维,类似代理设计模式。

3)Callable接口实现多线程
使用Runnable接口实现的多线程可以避免单继承的局限,但是还有一个问题就是run方法没有返回值,为了解决这个问题,所以提供了一个Callable接口java.util.concurrent.Callable

Callable接口定义:

@FunctionalIterface
public interface Callable<T>{
	public T call() throws Exception;
}

FutureTask类常用方法:

import java.util.concurrent.ExecutionException; // 导入ExecutionException异常包
public FutureTask(Callable<T> callable) // 构造函数:接收Callable接口实例
public FutureTask(Runable runnable,T result) // 构造函数:接收Runnable接口实例,同时指定返回结果类型 
public T get() throws InterruptedException,ExecutionException // 取得线程操作返回结果

Thread类的一个构造方法:

public Thread(FutureTask<T> futuretask) //构造方法:接收FutureTask实例化对象

参考代码:

package day07;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class ThreadTest {
    public static void main(String[] args) throws Exception{
        // 实例化继承Callable接口的MyThread类
        MyThread t1 = new MyThread("线程a");
        MyThread t2 = new MyThread("线程b");
        MyThread t3 = new MyThread("线程c");
        MyThread t4 = new MyThread("线程d");
        MyThread t5 = new MyThread("线程e");

        // FutureTask类接收继承Callable接口的MyThread的实例
        FutureTask<Integer> ft1 = new FutureTask<Integer>(t1);
        FutureTask<Integer> ft2 = new FutureTask<Integer>(t2);
        FutureTask<Integer> ft3 = new FutureTask<Integer>(t3);
        FutureTask<Integer> ft4 = new FutureTask<Integer>(t4);
        FutureTask<Integer> ft5 = new FutureTask<Integer>(t5);

        // 启动多线程
        new Thread(ft1).start();
        new Thread(ft2).start();
        new Thread(ft3).start();
        new Thread(ft4).start();
        new Thread(ft5).start();
        System.out.println(ft1.get());
        System.out.println(ft2.get());
        System.out.println(ft3.get());
        System.out.println(ft4.get());
        System.out.println(ft5.get());

    }
}

class MyThread implements Callable<Integer> {
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public Integer call() {
        Integer sum = 0;
        for (int i = 0; i < 100; i++) {
            System.out.println(this.name + i);
            sum += i;
        }
        return sum;
    }
}


输出:
线程e0
线程a0
线程b0
线程c0
线程d0
线程c1
线程b1
线程a1
线程e1
线程a2
线程b2
线程c2
线程d1
线程c3
线程b3
线程a3
线程e2
线程a4
线程b4
线程c4
线程d2
线程c5
线程b5
线程a5
线程e3
线程a6
线程b6
线程c6
线程d3
线程c7
线程b7
线程a7
线程e4
线程a8

通过代码分析,Callable接口实现采用泛型技术实现,继承需要重写call方法,再通过FutureTask包装器包装,传入后实例化Thread类实现多线程。
其中FutureTask类是Runnable接口的子类,所以才可以利用Thread类的start方法启动多线程,读者可以将call方法假设为有返回值的run方法。

2. Java线程状态
创建,就绪,运行,阻塞,死亡

3. 多线程常用操作方法
1)线程的命名与获取
线程是不确定的运行状态,名称就是线程的主要标记。因此,需要注意的是,对于线程的名字一定要在启动之前设置进程名称,不建议对已经启动的线程,进行更改名称,或者为不同线程设置重名。

主要方法:

public Thread(Runnable runnable,String name) //构造函数:实例化线程对象,为线程对象设置名称
public final void setName(String name) // 普通函数:设置线程名字
public final String getName() // 普通函数:获取线程名字

观察线程命名操作
参考代码:

package day10;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();
        MyThread m3 = new MyThread();

        new Thread(m1, "线程a").start();
        new Thread(m2, "线程b").start();
        new Thread(m3).start();
        m1.run();
    }
}
class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
        System.out.println(Thread.currentThread().getName());
    }
}

线程a
线程a
Thread-0
main
main
线程b
线程b
Thread-0

2)线程休眠方法
sleep方法定义在java.lang.Thread中,由Thread.sleep()调用实现。其作用是需要暂缓线程的执行速度,则可以让当前线程休眠,即当前线程从“运行状态”进入到“阻塞状态”。sleep方法会指定休眠时间,线程休眠的时间会大于或等于该休眠时间,该线程会被唤醒,此时它会由“阻塞状态”变成“就绪状态”,然后等待CPU的调度执行。

主要方法:

public static void sleep(long millis) throws InterruptedException // 普通函数:设置休眠时间的毫秒数
public static void sleep(long millis,int nanos) throws InterruptedException // 普通函数:设置休眠毫秒数和纳秒数
package day11;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();
        MyThread m3 = new MyThread();

        Thread t1 = new Thread(m1, "线程a");
        Thread t2 = new Thread(m2, "线程b");
        Thread t3 = new Thread(m3, "线程c");

        t1.start();
        t2.start();
        t3.start();
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "正在工作中");

            if (i == 20) {
                try {
                    System.out.println(Thread.currentThread().getName() + "等会儿就要休息5秒");
                    Thread.sleep(5000);
                    System.out.println(Thread.currentThread().getName() + "已经休息5秒了");
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + "休眠被打扰了");
                }
            }
        }
    }
}

输出:
线程a正在工作中
线程c等会儿就要休息5秒
线程b正在工作中
线程a正在工作中
线程b正在工作中
线程a正在工作中
线程b正在工作中
线程a正在工作中
线程b正在工作中
线程a等会儿就要休息5秒
线程b正在工作中
线程b等会儿就要休息5秒
线程a已经休息5秒了
线程a正在工作中
线程b已经休息5秒了
线程b正在工作中
线程b正在工作中
线程b正在工作中
线程b正在工作中
线程b正在工作中
线程b正在工作中
线程b正在工作中
线程c已经休息5秒了
线程c正在工作中

根据输出结果发现,休眠期间该线程并没有工作,而其他未休眠的线程则在继续工作。

3)线程中断方法
interrupt方法定义在java.lang.Thread中,由Thread.interrupt()调用实现。该方法将会设置该线程的中断状态位,即设置为true,中断的结果线程是终止状态、还是阻塞状态或是继续运行至下一步,就取决于该程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(即中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程。

主要方法:

// 以下均为Thread类的方法
public boolean isInterrupted() //普通函数:判断线程是否被中断
public void interrupt() //普通函数:中断线程执行
package day11;

public class ThreadTest {
    public static void main(String[] args) throws Exception{
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();
        MyThread m3 = new MyThread();

        Thread t1 = new Thread(m1, "线程a");
        Thread t2 = new Thread(m2, "线程b");
        Thread t3 = new Thread(m3, "线程c");

        t1.start();
        t2.start();
        t3.start();

        Thread.sleep(6000);
        if (!t1.isInterrupted()) {
            System.out.println("吵闹");
            t1.interrupt();
        }
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        int i = 0;
        while (true) {

            System.out.println(Thread.currentThread().getName() + "正在工作中---" + i++);

            try {
                System.out.println(Thread.currentThread().getName() + "准备休息5秒");
                Thread.sleep(5000);
                System.out.println(Thread.currentThread().getName() + "已经休息5秒了");
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "被打扰了,不想工作了");
                break;
            }
        }
    }
}

输出:
线程a正在工作中---0
线程b正在工作中---0
线程c正在工作中---0
线程a准备休息5秒
线程b准备休息5秒
线程c准备休息5秒
线程a已经休息5秒了
线程a正在工作中---1
线程b已经休息5秒了
线程b正在工作中---1
线程c已经休息5秒了
线程c正在工作中---1
线程b准备休息5秒
线程a准备休息5秒
线程c准备休息5秒
吵闹
线程a被打扰了,不想工作了
线程b已经休息5秒了
线程b正在工作中---2
线程b准备休息5秒
线程c已经休息5秒了
线程c正在工作中---2
线程c准备休息5秒
线程b已经休息5秒了
线程b正在工作中---3
线程c已经休息5秒了
线程c正在工作中---3
线程c准备休息5

根据输出结果发现,线程一在休眠期间被中断,然后直接break,也就是说以后就只有线程二和三工作了。这里每当中断执行都会产生InterruptedException异常。

4)线程强制执行
join方法定义在java.lang.Thread中,由Thread.join()调用实现。多线程启动后会交替进行资源抢占和线程体执行,如果此时某些线程异常重要,也就是说这个对象需要优先执行完成,则可以设置为线程强制执行,待其完成后其它线程继续执行。

可参考多线程的join方法 //https://blog.csdn.net/weixin_46410481/article/details/120398845
主要方法:

// Thread类方法
public final void join() throws InterruptedException //普通函数:强制执行

参考代码:

package day11;

public class ThreadTest {
    public static void main(String[] args) throws Exception{
        Thread mainThread = Thread.currentThread();

        MyThread m1 = new MyThread(mainThread);
        Thread t1 = new Thread(m1, "子线程");

        t1.start();

        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep(1000); //每次main线程休眠1秒
                System.out.println(Thread.currentThread().getName() + "正在工作中----" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("我main线程已经完成了所有任务,从此无法再复生了----");
    }
}
class MyThread implements Runnable {
    private Thread thread = null;
    public MyThread() {}
    public  MyThread(Thread thread) {
        this.thread = thread;
    }
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            if (i >= 20 && i <= 25) {
                try {
                    System.out.println(thread.getName() + "被迫参与" + Thread.currentThread().getName() + "的工作-----" + i);
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 以下语句不管上面判断语句是否执行都会执行的
            System.out.println(Thread.currentThread().getName() + "正在工作中----" + i);
        }
    }
}

输出:
子线程正在工作中----17
子线程正在工作中----18
子线程正在工作中----19
main被迫参与子线程的工作-----20
main正在工作中----0
main正在工作中----1
main正在工作中----2
main正在工作中----3
main正在工作中----4
main正在工作中----5
main正在工作中----6
main正在工作中----7
main正在工作中----8
main正在工作中----9
main正在工作中----10
main正在工作中----11
main正在工作中----12
main正在工作中----13
main正在工作中----14
main正在工作中----15
main正在工作中----16
main正在工作中----17
main正在工作中----18
main正在工作中----19
我main线程已经完成了所有任务,从此无法再复生了----
子线程正在工作中----20
main被迫参与子线程的工作-----21
子线程正在工作中----21
main被迫参与子线程的工作-----22
子线程正在工作中----22
main被迫参与子线程的工作-----23
子线程正在工作中----23
main被迫参与子线程的工作-----24
子线程正在工作中----24
main被迫参与子线程的工作-----25
子线程正在工作中----25
子线程正在工作中----26

根据输出结果发现,当子线程经行到第20次时,开始强制执行main线程任务,直至全部完成为止,而且只能够完整地执行地一次。当main线程执行完毕后,子线程才能开始继续执行。

多线程中Thread的join方法

join方法是Thread类中的一个方法,该方法的定义是等待该线程执行直到终止。其实就说join方法将挂起调用线程的执行,直到被调用的对象完成它的执行。

join实例(以一道面试题为例)

现在有t1、t2、t3三个线程,你怎样保证T2在T1执行完之后执行,T3在T2执行完后执行?这个问题是网上很热门的面试题(这里除了join还有很多方法能够实现,只是使用join是最简单的方案),下面是实现的代码:

参考代码:

package day10;

public class ThreadTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyThread());
        t1.start();
        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Thread t2 = new Thread(new MyThread());
        t2.start();
        try {
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread t3 = new Thread(new MyThread());
        t3.start();
    }
}
class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---is running");
    }
}

输出:
Thread-0---is running
Thread-1---is running
Thread-2---is running

或者:
package day10;

public class ThreadTest {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "---is running");
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + "---is running");
                }
            }
        });

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + "---is running");
                }
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

输出:
Thread-0---is running
Thread-1---is running
Thread-2---is running

5)线程让步
yield方法定义在java.lang.Thread中,由Thread.yield()调用实现。多线程在彼此交替执行的时候往往需要进行资源的轮流抢占,如果某些不是很重要的线程抢占到资源但是又不急于执行时,就可以将当前的资源暂时让步出去,交给其它资源先执行。但是,因为yeild是将线程由“运行状态”转别为“就绪状态”,这样并不能保证在当前线程调用yield方法之后,其它具有相同优先级的线程就一定能获得执行权,也有可能是当前线程又进入到“运行状态”继续运行,因为还是要依靠CPU调度才可以。

主要方法:

public static void yield() // 静态函数:线程让步

参考代码:

package day12;

public class ThreadTest {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        Runnable mt1 = new MyThread(mainThread);
        Thread thread1 = new Thread(mt1,"子线程");

        thread1.start();
        for(int i = 0 ; i < 40 ; i++) {
            System.out.println(Thread.currentThread().getName() + " 正在工作中……" + i);
        }
    }
}

class MyThread implements Runnable{
    private Thread thread = null;
    public MyThread () {}
    public MyThread(Thread thread) {
        this.thread = thread;
    }
    @Override
    public void run() {
        for(int i = 0 ; i<50 ; i++) {
            System.out.println(Thread.currentThread().getName()  + " 正在工作中……" + i);
            if( i  == 30) {
                System.out.println(Thread.currentThread().getName() + " 打算将工作交给 "+thread.getName() + "了……");
                Thread.yield(); // 当前线程让步出去
                System.out.println(Thread.currentThread().getName() + " 又想自己工作了……");
            }
        }
    }
}

输出:
main 正在工作中……26
子线程 正在工作中……27
main 正在工作中……27
子线程 正在工作中……28
main 正在工作中……28
子线程 正在工作中……29
main 正在工作中……29
子线程 正在工作中……30
main 正在工作中……30
main 正在工作中……31
main 正在工作中……32
main 正在工作中……33
main 正在工作中……34
main 正在工作中……35
main 正在工作中……36
main 正在工作中……37
main 正在工作中……38
main 正在工作中……39
子线程 打算将工作交给 main了……
子线程 又想自己工作了……
子线程 正在工作中……31
子线程 正在工作中……32
子线程 正在工作中……33
子线程 正在工作中……34
子线程 正在工作中……35

根据输出结果发现,会暂时将资源调度让给其它的线程,当然这并不是严格的,因为还是要CPU调度的

6)线程优先级
所有创造的线程都是子线程,所有的子线程在启动时都会保持同样的优先级权限,但是如果现在某些重要的线程希望可以优先抢占到资源并且先执行,就可以修改优先级权限来实现。当线程的优先级没有指定时,所有线程都携带普通优先级。

注意:
优先级用从1到10的范围的整数指定。10表示最高优先级,1表示最低优先级,5是普通优先级,也就是默认优先级。
优先级相对最高的线程在执行时被给予优先权限。但是不能保证线程在启动时就进入运行状态。
优先级越高越有可能先执行

主要方法:

public static final int MAX_PRIORITY // 静态常量:最高优先级,数值为10
public static final int NORM_PRIORITY //静态常量:普通优先级,数值为5
public static final int MIN_PRIORITY // 静态常量:最低优先级,数值为1
public final void setPriority(int newPriority) // 普通函数:设置优先级
public final int getPriority() //普通函数:获取优先级

参考代码:

package day13;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();

        Thread t1 = new Thread(m1, "线程a");
        Thread t2 = new Thread(m2, "线程b");

        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);

        t1.start();
        t2.start();
    }
}
class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "---正在工作中" + i);
        }
    }
}

输出:
线程b---正在工作中0
线程a---正在工作中0
线程b---正在工作中1
线程a---正在工作中1
线程b---正在工作中2
线程a---正在工作中2
线程b---正在工作中3
线程a---正在工作中3

4. 线程的同步和锁死
1)线程的同步
解决数据共享问题必须使用同步,所谓的同步就是指多个线程在同一个时间段内只能有一个线程执行指定的代码,其他线程要等待此线程完成之后才可以继续进行执行,在Java中提供有synchronized关键字以实现同步处理,同步的关键是要为代码加上锁。

而锁的操作有三种:
1.同步代码块
2.同步方法
3.Lock实现

同步代码块实现

使用方式:

synchronized(需要同步的对象){
    需要同步的操作
}
package day14;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();

        new Thread(m1, "售票员a").start();
        new Thread(m1, "售票员b").start();
        new Thread(m1, "售票员c").start();
    }
}

class MyThread implements Runnable {
    private int tickets = 10;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (tickets < 0) {
                    System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:" + tickets);
                    break;
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() +  " 正在售票,还剩余票数为:" + tickets--);
            }
        }
    }
}

输出:
售票员a 正在售票,还剩余票数为:10
售票员a 正在售票,还剩余票数为:9
售票员a 正在售票,还剩余票数为:8
售票员a 正在售票,还剩余票数为:7
售票员a 正在售票,还剩余票数为:6
售票员a 正在售票,还剩余票数为:5
售票员a 正在售票,还剩余票数为:4
售票员a 正在售票,还剩余票数为:3
售票员a 正在售票,还剩余票数为:2
售票员a 正在售票,还剩余票数为:1
售票员a 正在售票,还剩余票数为:0
售票员a的票已经全部售完,此时的票数量为:-1
售票员c的票已经全部售完,此时的票数量为:-1
售票员b的票已经全部售完,此时的票数量为:-1

同步方法实现
使用方式:

利用函数包装的形式实现,如下:
修饰符 synchronized 返回类型 函数名()

参考代码:

package day15;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();

        new Thread(m1, "售票员a").start();
        new Thread(m1, "售票员b").start();
        new Thread(m1, "售票员c").start();
    }
}

class MyThread implements Runnable {
    private int ticket = 10;
    @Override
    public void run() {
        while (this.sale()) {
        }
    }
    public synchronized boolean sale() {
        if(ticket<0) {
            System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:"+ticket);
            return false;
        }
        try {
            Thread.sleep(10); // 延迟0.01秒,使得ticket可以被其它线程充分改变(可能此时的ticket小于等于0了)
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()  + " 正在售票,还剩余票数为:" + ticket--);
        return true;
    }
}

输出:
售票员a 正在售票,还剩余票数为:10
售票员a 正在售票,还剩余票数为:9
售票员a 正在售票,还剩余票数为:8
售票员a 正在售票,还剩余票数为:7
售票员a 正在售票,还剩余票数为:6
售票员a 正在售票,还剩余票数为:5
售票员a 正在售票,还剩余票数为:4
售票员a 正在售票,还剩余票数为:3
售票员a 正在售票,还剩余票数为:2
售票员a 正在售票,还剩余票数为:1
售票员a 正在售票,还剩余票数为:0
售票员a的票已经全部售完,此时的票数量为:-1
售票员c的票已经全部售完,此时的票数量为:-1
售票员b的票已经全部售完,此时的票数量为:-1

进程已结束,退出代码0

Lock锁实现

package day16;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread m1 = new MyThread();

        new Thread(m1, "售票员a").start();
        new Thread(m1, "售票员b").start();
        new Thread(m1, "售票员c").start();
    }
}
class MyThread implements Runnable {
    private int tickets = 10;
    private final Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (this.sale()) {
        }
    }
    public boolean sale () {
        lock.lock();
        try {
            if (tickets < 0) {
                System.out.println(Thread.currentThread().getName() + "的票已经全部售完,此时的票数量为:"+tickets);
                return false;
            }
            Thread.sleep(200);
            System.out.println(Thread.currentThread().getName() + "正在售票,还剩余票数为:" + tickets--);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return true;
    }
}

输出:
售票员a正在售票,还剩余票数为:10
售票员a正在售票,还剩余票数为:9
售票员a正在售票,还剩余票数为:8
售票员a正在售票,还剩余票数为:7
售票员a正在售票,还剩余票数为:6
售票员a正在售票,还剩余票数为:5
售票员a正在售票,还剩余票数为:4
售票员a正在售票,还剩余票数为:3
售票员a正在售票,还剩余票数为:2
售票员a正在售票,还剩余票数为:1
售票员a正在售票,还剩余票数为:0
售票员a的票已经全部售完,此时的票数量为:-1
售票员b的票已经全部售完,此时的票数量为:-1
售票员c的票已经全部售完,此时的票数量为:-1

5. 后台守护线程

参考代码:

package day17;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread m1 = new MyThread(8);
        MyThread m2 = new MyThread(100);

        Thread t1 = new Thread(m1, "用户线程");
        Thread t2 = new Thread(m2, "守护线程");
        t2.setDaemon(true);

        t1.start();
        t2.start();
    }
}

class MyThread implements Runnable {
    private int times;
    public MyThread(int times) {
        this.times = times;
    }

    @Override
    public void run() {
        for (int i = 0; i < times; i++) {
            if (Thread.currentThread().isDaemon()) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "正在工作中---" + i);
        }
    }
}

输出:
用户线程正在工作中---0
用户线程正在工作中---1
用户线程正在工作中---2
用户线程正在工作中---3
用户线程正在工作中---4
用户线程正在工作中---5
守护线程正在工作中---0
用户线程正在工作中---6
用户线程正在工作中---7

根据结果发现,守护线程已经提前结束了,原因是main线程等用户线程全部消失了。

守护线程的应用场景

在主线程关闭后无需手动关闭守护线程,因为会自动关闭,避免了麻烦,Java垃圾回收线程就是一个典型的守护线程,简单粗暴地可以理解为所有为线程服务而不涉及资源的线程都能设置为守护线程。

6. 线程池
1)线程池的概念
容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程的操作,无需反复创建线程而消耗过多的资源。

2)为何引入线程池?
如果并发的线程数量过多,并且每个线程都是执行一个时间很短的任务就结束,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要消耗时间,线程也属于宝贵的系统资源,因此,线程池就是为了能使线程可以复用而创建的。

3)线程池的好处?

  1. 降低资源的消耗,减少创建和销毁线程的次数,每个工作线程都可以被重复使用,可执行多个任务
  2. 提高响应速度,不需要频繁地创建线程,如果有线程可以直接使用,避免了系统僵死
  3. 提高线程的可管理性

核心思想:线程复用

参考代码:

package day18;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadTest {
    public static void main(String[] args) {
        // 1.创建一个线程池,指定线程的数量为5
        ExecutorService pools = Executors.newFixedThreadPool(5);
        // 2.添加线程任务
        Runnable target = new MyThread();

        pools.submit(target); // 第一次提交任务,此时创建新线程
        pools.submit(target);
        pools.submit(target);
        pools.submit(target);
        pools.submit(target);
        pools.shutdown();
    }
}
class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "正在执行任务…  " + i);
        }
    }
}

输出:
pool-1-thread-1正在执行任务…  0
pool-1-thread-3正在执行任务…  0
pool-1-thread-3正在执行任务…  1
pool-1-thread-5正在执行任务…  0
pool-1-thread-2正在执行任务…  0
pool-1-thread-4正在执行任务…  0
pool-1-thread-2正在执行任务…  1
pool-1-thread-2正在执行任务…  2
pool-1-thread-2正在执行任务…  3
pool-1-thread-5正在执行任务…  1
pool-1-thread-3正在执行任务…  2
pool-1-thread-1正在执行任务…  1
pool-1-thread-3正在执行任务…  3
pool-1-thread-1正在执行任务…  2
pool-1-thread-5正在执行任务…  2
pool-1-thread-2正在执行任务…  4
pool-1-thread-4正在执行任务…  1
pool-1-thread-2正在执行任务…  5
pool-1-thread-5正在执行任务…  3
pool-1-thread-1正在执行任务…  3
pool-1-thread-3正在执行任务…  4
pool-1-thread-1正在执行任务…  4
pool-1-thread-5正在执行任务…  4
pool-1-thread-2正在执行任务…  6
pool-1-thread-4正在执行任务…  2
pool-1-thread-2正在执行任务…  7
pool-1-thread-4正在执行任务…  3
pool-1-thread-5正在执行任务…  5
pool-1-thread-1正在执行任务…  5
pool-1-thread-3正在执行任务…  5
pool-1-thread-1正在执行任务…  6
pool-1-thread-4正在执行任务…  4
pool-1-thread-5正在执行任务…  6
pool-1-thread-4正在执行任务…  5
pool-1-thread-2正在执行任务…  8
pool-1-thread-4正在执行任务…  6
pool-1-thread-2正在执行任务…  9
pool-1-thread-5正在执行任务…  7
pool-1-thread-1正在执行任务…  7
pool-1-thread-3正在执行任务…  6
pool-1-thread-1正在执行任务…  8
pool-1-thread-3正在执行任务…  7
pool-1-thread-1正在执行任务…  9
pool-1-thread-5正在执行任务…  8
pool-1-thread-4正在执行任务…  7
pool-1-thread-5正在执行任务…  9
pool-1-thread-3正在执行任务…  8
pool-1-thread-3正在执行任务…  9
pool-1-thread-4正在执行任务…  8
pool-1-thread-4正在执行任务…  9

参考链接:
https://blog.csdn.net/weixin_43914658/article/details/109449580#t17

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java多线程编程是指在Java语言中使用多个线程来同时执行多个任务,以提高程序的并发性能和响应速度。Java多线程编程PDF是一本介绍Java多线程编程的PDF文档,其中包含了Java多线程编程的基本概念、原理、技术和实践经验。该PDF文档可以帮助读者快速了解Java多线程编程的相关知识,并提供实用的编程示例和案例分析,有助于读者掌握Java多线程编程的核心技术和方法。 在Java多线程编程PDF中,读者可以学习到如何创建和启动线程、线程的状态和生命周期、线程间的通信与同步、线程池的使用、并发容器等相关内容。同时,该PDF文档还介绍了Java中的并发包(concurrent package)的使用和实现原理,以及多线程编程中的常见问题和解决方案。 通过学习Java多线程编程PDF,读者可以深入了解Java多线程编程的理论和实践,掌握多线程编程的核心知识和技能,提高自己的并发编程能力,为开发高性能、高并发的Java应用程序打下坚实的基础。同时,对于已经掌握多线程编程知识的读者来说,该PDF文档也能够帮助他们进一步巩固和扩展自己的多线程编程技能,提升自己的编程水平和竞争力。 总之,Java多线程编程PDF是一本全面介绍Java多线程编程的优秀文档,对于Java程序员来说具有很高的参考价值,可以帮助他们在多线程编程领域取得更好的成就。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值