• Java 线程


    线程

    创建线程的方式

    1. 继承Thread,并实现run方法;new一个对象,调用start()方法,开启一个线程

    2. 实现Runnable接口,并实现run 方法,new Thread对象,把Runnable的实现类放到里面,然后调用Thread的start方法。

    1. public class useThread {
    2.    public static void main(String[] args) {
    3. //       extendThread extendThread = new extendThread();
    4. //       extendThread extendThread1 = new extendThread();
    5. //       extendThread.setName("thread 1");
    6. //       extendThread1.setName("thread 2");
    7. //
    8. //       // 调用 run方法,只是普通的调用方法,调用start才是启动一个新的线程。
    9. //       extendThread.start();
    10. //       extendThread1.start();
    11.        importThread importThread = new importThread();
    12.        importThread importThread1 = new importThread();
    13.        Thread t1 = new Thread(importThread);
    14.        Thread t2 = new Thread(importThread1);
    15.        t1.setName("thead 1");
    16.        t2.setName("thread 2");
    17.        t1.start();
    18.        t2.start();
    19.   }
    20. }
    21. //继承
    22. class extendThread extends Thread{
    23.    //从写run
    24.    @Override
    25.    public void run() {
    26.        for(int i = 0 ;i < 100 ;i ++ )
    27.            System.out.println( getName() +"继承Thread类");
    28.   }
    29. }
    30. //实现
    31. class importThread implements Runnable{
    32.    @Override
    33.    public void run() {
    34.        for (int i = 0; i < 100; i++){
    35.            //获得当前线程
    36.            Thread thread = Thread.currentThread();
    37.            System.out.println(thread.getName() + "Hello,world!");
    38.       }
    39.   }
    40. }
    1. 使用Callable 和 Future;这种方式可以获得多线程运行的结果;创建一个类实现Callable ,泛型是多线程要返回的结果;创建一个类,实现Callable接口,重写call方法,创建一个实现类的对象(表示要执行的对象),创建FutureTask对象(管理多线程运行的结果),创建Thread类对象,启动;通过FutureTask对象可以获得线程执行的结果。

    1. public class useThread {
    2.    public static void main(String[] args) {
    3.        myCallable myCallable = new myCallable();
    4.        FutureTask ft = new FutureTask<>(myCallable);
    5.        Thread thread = new Thread(ft);
    6.        thread.start();
    7.        //获取线程调用结果
    8.        try {
    9.            System.out.println("多线程调用的结果:" + ft.get());
    10.       } catch (InterruptedException e) {
    11.            throw new RuntimeException(e);
    12.       } catch (ExecutionException e) {
    13.            throw new RuntimeException(e);
    14.       }
    15.   }
    16. }
    17. class myCallable implements Callable {
    18.    /**
    19.     * Computes a result, or throws an exception if unable to do so.
    20.     *
    21.     * @return computed result
    22.     * @throws Exception if unable to compute a result
    23.     */
    24.    @Override
    25.    public Integer call() throws Exception {
    26.        int sum = 0;
    27.        for(int i = 1;i <= 100 ;i +=1 ) sum += i;
    28.        return sum;
    29.   }
    30. }

    选择:

    简单就选继承Thread,需要扩展用Runable接口;需要结果就Callable。

    常用的成员方法

    1. getName() ,可以使用t.setName('')设置线程名字,默认是Thread-0。

    2. setName()或者创建对象的使用可以使用 Thread(String name) 这个构造器。

      1. 构造方法是不能继承的,如果子类想要使用父类的构造方法,可以自己写一个构造方法,然后去调用父类的构造方法。

    3. currentThread() -- static获得当前运行的线程,可以获得线程信息。

      1. 当JVM启动后,会自动调用多条线程,其中一条线程就是main线程,它的作用就是执行main方法,并执行里面的内容。

    4. sleep() -- static让线程休眠

      1. 那条线程执行这个方法,就停留多少时间,单位是毫秒。

      2. 当时间到了之后,会继续执行下面的代码。

      3. 父类中的方法没有抛出异常,继承后的子类方法,也就不能抛出异常,必须使用try-catch

    5. setPriority(int newPriority) 设置线程的优先级。

      1. 默认是5,最大是10,最小是1。

      2. priority越大,被执行的概率越高,但并不是按照比例去执行。

    6. setDaemon(boolean on) 设置为守护线程。

      1. 当其他的非守护线程执行完毕之后,守护线程会陆续结束。

      2. 应用场景:聊天和文件的上传,当聊天关闭的时候,文件也没有上传的必要了,所以:在这里文件上传就是守护线程。

    7. yield() -- static 出让线程

      1. 只是出让,但是不一定也会让别人抢到线程。

    8. join() 插入线程

      1. 插入线程到当前线程之前,只有等到插入的线程执行完之后,才会执行当前线程。

    线程的生命周期

    不安全的问题

    1. public class part1 {
    2.    public static void main(String[] args) throws InterruptedException {
    3.        myThread t1 = new myThread("window1");
    4.        myThread t2 = new myThread("window2");
    5.        myThread t3 = new myThread("window3");
    6.        t2.start();
    7.        t1.start();
    8.        t3.start();
    9.   }
    10. }
    11. class myThread extends Thread{
    12.    public myThread(String name){
    13.        super(name);
    14.   }
    15.    public myThread(){
    16.        super();
    17.   }
    18.    static int ticket = 0;
    19.    @Override
    20.    public void run() {
    21.        while(true){
    22.            if(ticket < 100){
    23.                try {
    24.                    Thread.sleep(100);
    25.               }catch (InterruptedException e){
    26.                    System.err.println("kkkkk");
    27.               }
    28.                ticket ++;
    29.                System.out.println( getName() +  "已经买到了 ===> " + ticket);
    30.           }else{
    31.                break;
    32.           }
    33.       }
    34.   }
    35. }

    出现重复售票

    • cpu的执行权,随时可能被其他线程抢占。

    票多买

    • 在99的时候,通过有多个线程进入while循环。

    解决方案:---- 加锁

    同步代码块

    synchronized(里的锁对象一定要是唯一的)

    1. public class part1 {
    2.    public static void main(String[] args) throws InterruptedException {
    3.        myThread t1 = new myThread("window1");
    4.        myThread t2 = new myThread("window2");
    5.        myThread t3 = new myThread("window3");
    6.        t2.start();
    7.        t1.start();
    8.        t3.start();
    9.   }
    10. }
    11. class myThread extends Thread{
    12.    public myThread(String name){
    13.        super(name);
    14.   }
    15.    public myThread(){
    16.        super();
    17.   }
    18.    static int ticket = 0;
    19.    //锁对象一定是唯一的。
    20.    static Object obj = new Object();
    21.    @Override
    22.    public void run() {
    23.        while(true){
    24.            synchronized (obj){
    25.                if(ticket < 100){
    26.                    //锁住操作票的代码
    27.                    try {
    28.                        Thread.sleep(1);
    29.                   } catch (InterruptedException e) {
    30.                        e.printStackTrace();
    31.                   }
    32.                    System.out.println(Thread.currentThread().getName() + "窗口出售第" + ++ticket + "票");
    33.               }else{
    34.                    break;
    35.               }
    36.           }
    37.       }
    38.   }
    39. }

    同步方法

    修饰符 synchronized 返回值类型 方法名() {....}

    特点

    1. 同步方法锁住的是方法里的全部代码

    2. 锁对象不能指定:

      1. 非静态: this

      2. 静态: class

    锁对象是:this,也能保持线程同步,因为这里始终都是一个对象。

    1. public class part1 {
    2.    public static void main(String[] args) throws InterruptedException {
    3.        myThread1 myThread1 = new myThread1();
    4.        new Thread(myThread1,"窗口2").start();
    5.        new Thread(myThread1,"窗口1").start();
    6.   }
    7. }
    8. class myThread1 implements Runnable{
    9.    int ticket = 0 ;   //注意这里的ticket并不用一定是静态的,因为这个对象创建后,是作为参数在Thread执行的,只有一份
    10.    @Override
    11.    public void run() {
    12.        while(true){
    13.            if (extracted()) break;
    14.       }
    15.   }
    16.    private synchronized boolean extracted() {
    17.        if(ticket < 100){
    18.            //锁住操作票的代码
    19.            try {
    20.                Thread.sleep(1);
    21.           } catch (InterruptedException e) {
    22.                e.printStackTrace();
    23.           }
    24.            System.out.println(Thread.currentThread().getName() + "窗口出售第" + ++ticket + "票");
    25.       }else{
    26.            return true;
    27.       }
    28.        return false;
    29.   }
    30. }

    Lock

    1. public class part1 {
    2.    public static void main(String[] args) throws InterruptedException {
    3.        new myThread("窗口1").start();
    4.        new myThread("窗口2").start();
    5.   }
    6. }
    7. class myThread extends Thread{
    8.    public myThread(String name){
    9.        super(name);
    10.   }
    11.    public myThread(){
    12.        super();
    13.   }
    14.    static int ticket = 0;
    15.    //需要共享同一把锁
    16.    static Lock lock = new ReentrantLock();
    17.    @Override
    18.    public void run() {
    19.        while(true){
    20.            //锁住操作票的代码
    21.            lock.lock();
    22.            if(ticket < 100){
    23.                try {
    24.                    Thread.sleep(1);
    25.               } catch (InterruptedException e) {
    26.                    e.printStackTrace();
    27.               }
    28.                System.out.println(Thread.currentThread().getName() + "窗口出售第" + ++ticket + "票");
    29.           }else{
    30.                break;
    31.           }
    32.            lock.unlock();
    33.       }
    34.   }
    35. }

    上面这段代码有点问题,如果通过if break了,跳出循环后,就会导致锁没有释放,然后就会导致程序堵塞,稳妥的写法是把释放锁的操作放的finally中进行,因为无论如何finally中的代码肯定是要执行的。

    1. @Override
    2. public void run() {
    3.    while(true){
    4.        //锁住操作票的代码
    5.        lock.lock();
    6.        try {
    7.            if(ticket < 100){
    8.                try {
    9.                    Thread.sleep(1);
    10.               } catch (InterruptedException e) {
    11.                    e.printStackTrace();
    12.               }
    13.                System.out.println(Thread.currentThread().getName() + "窗口出售第" + ++ticket + "票");
    14.           }else{
    15.                break;
    16.           }
    17.       } catch (Exception e) {
    18.            throw new RuntimeException(e);
    19.       } finally {
    20.            lock.unlock();
    21.       }
    22.   }
    23. }

    死锁

    死锁是一个常见的错误。

    1. public class part1 {
    2.    public static void main(String[] args) throws InterruptedException {
    3.        new myThread("a").start();
    4.        new myThread("b").start();
    5.   }
    6. }
    7. class myThread extends Thread{
    8.    public myThread(String name){
    9.        super(name);
    10.   }
    11.    static Object objA = new Object();
    12.    static Object objB = new Object();
    13.    @Override
    14.    public void run() {
    15.        while(true){
    16.            if("a".equals(this.getName())){
    17.                synchronized (objA){
    18.                    System.out.println("线程A拿到A锁,准备拿B锁");
    19.                    synchronized (objB){
    20.                        System.out.println("线程A拿到B锁,执行完成!");
    21.                   }
    22.               }
    23.           }else if("b".equals(this.getName())){
    24.                synchronized (objB){
    25.                    System.out.println("线程B拿到B锁,准备拿A锁");
    26.                    synchronized (objA){
    27.                        System.out.println("线程B拿到A锁,执行完成!");
    28.                   }
    29.               }
    30.           }
    31.       }
    32.   }
    33. }

    生产者和消费者

    生产者消费者模式是一个十分经典的多线程协作的模式。

    生产者

    1. //生产者线程
    2. public class Cook extends Thread{
    3.    @Override
    4.    public void run() {
    5.        while(true){
    6.            synchronized (dest.lock){
    7.                if(dest.count == 0){
    8.                    break;
    9.               }else{
    10.                    //判断桌子上是否有食物
    11.                    if(dest.status == 1){
    12.                        try {
    13.                            dest.lock.wait();
    14.                       } catch (InterruptedException e) {
    15.                            e.printStackTrace();
    16.                       }
    17.                   }else{
    18.                        //没有食物
    19.                        System.out.println("做了一碗");
    20.                        //修改桌子上食物的状态
    21.                        dest.status = 1;
    22.                        //叫醒消费者
    23.                        dest.lock.notifyAll();
    24.                   }
    25.               }
    26.           }
    27.       }
    28.   }
    29. }

    消费者

    1. public class Foodie extends Thread{
    2.    public void run() {
    3.        /*
    4.            1.循环生产
    5.            2.同步代码
    6.            3.判断共享数据
    7.         */
    8.        while(true){
    9.            synchronized (dest.lock){
    10.                if(dest.count == 0)
    11.                    break;
    12.                else{
    13.                    //判断桌子上是否有面条
    14.                    if(dest.status == 0){
    15.                        // 没有就等待,唤醒厨师
    16.                        System.out.println("厨师生产面条");
    17.                        try {
    18.                            dest.lock.wait();  //让当前线程根锁进行绑定
    19.                       } catch (InterruptedException e) {
    20.                            e.printStackTrace();
    21.                       }
    22.                   }else{
    23.                        //把吃的总数--
    24.                        dest.count -- ;
    25.                        // 有就开吃
    26.                        System.out.println("开吃,开吃,还剩下 " + dest.count+ " 碗");
    27.                        // 吃完之后,厨师继续做饭
    28.                        dest.lock.notifyAll();  //唤醒所有的线程
    29.                        //修改桌子的状态
    30.                        dest.status = 0;
    31.                   }
    32.               }
    33.           }
    34.       }
    35.   }
    36. }
    37. //控制生产者和消费者的执行
    38. public class dest {
    39.    
    40.    public static int status = 0;
    41.    public static int count = 10 ;
    42.    //锁对象
    43.    public static final Object lock = new Object();
    44. }

    等待唤醒机制(阻塞队列)

    生产者

    1. public class Cook extends Thread{
    2.    ArrayBlockingQueue queue;
    3.    public Cook(ArrayBlockingQueue queue) {
    4.        this.queue = queue;
    5.   }
    6.    @Override
    7.    public void run() {
    8.        while(true){
    9.            //不断地把面条放到阻塞队列中去
    10.            try{
    11.                queue.put("面条");
    12.                System.out.println("厨师放了一碗面条");
    13.           } catch(Exception e){
    14.                e.printStackTrace();
    15.           }
    16.       }
    17.   }
    18. }

    消费者

    1. public class Foodie extends Thread{
    2.    ArrayBlockingQueue queue;
    3.    public Foodie(ArrayBlockingQueue queue)
    4.   {
    5.        this.queue = queue;
    6.   }
    7.    @Override
    8.    public void run() {
    9.        while(true){
    10.            try {
    11.                String peek = queue.take();
    12.                System.out.println(peek);
    13.           }catch (Exception  e){
    14.                e.printStackTrace();
    15.           }
    16.       }
    17.   }
    18. }

    Test

    1. public class Test {
    2.    public static void main(String[] args) {
    3.        ArrayBlockingQueue<String> strings = new ArrayBlockingQueue<String>(3);
    4.        Cook cook = new Cook(strings);
    5.        Foodie foodie = new Foodie(strings);
    6.        cook.start();
    7.        foodie.start();
    8.   }
    9. }

    ArrayBlockingQueueLinkedBlockingQueue实现的接口有Queue,Collection,BlockingQueue和Iterator等接口。

    ArrayBlockingQueue 底层是数组,需要指定队列大小。

    put放入一个元素。

    1. public void put(E e) throws InterruptedException {
    2.    checkNotNull(e);
    3.    final ReentrantLock lock = this.lock;
    4.    lock.lockInterruptibly();
    5.    try {
    6.        //如果当前队列是满的,就等待。
    7.        while (count == items.length)
    8.            notFull.await();
    9.        //否则就入队。
    10.        enqueue(e);
    11.   } finally {
    12.        //最后释放锁。
    13.        lock.unlock();
    14.   }
    15. }

    take往队列拿出一个元素。

    1. public E take() throws InterruptedException {
    2.    final ReentrantLock lock = this.lock;
    3.    lock.lockInterruptibly();
    4.    try {
    5.        //如果队列长度是0,就等待。
    6.        while (count == 0)
    7.            notEmpty.await();
    8.        //否则就拿出一个元素。
    9.        return dequeue();
    10.   } finally {
    11.        //最后释放锁
    12.        lock.unlock();
    13.   }
    14. }

    线程的状态

    1. new -- 新建 -- 创建线程对象

    2. Runable -- 可执行状态 -- start方法

    3. Blocked -- 阻塞状态 -- 无法获得锁对象

    4. Waiting -- 等待状态 -- wait方法

    5. Timed_Waitting -- 计时等待状态 -- sleep方法

    6. Terminated -- 结束状态 -- 全部代码执行完毕

  • 相关阅读:
    (完全解决)为什么二阶行列式的绝对值为面积
    「我的AIGC咒语库:分享和AI对话交流的秘诀——如何利用Prompt和AI进行高效交流?」
    51单片机+DS1302设计一个电子钟(LCD1602显示时间)
    Java面试过程中高频基础面试题
    jetson ubuntu 设置 usb声卡为默认声卡
    JavaScript学习总结(内置对象、简单数据类型和复杂数据类型)
    【ML on Kubernetes】第 10 章:构建、部署和监控模型
    Java运算符及流程控制
    python常见面试考点
    NLA自然语言分析实现数据分析零门槛
  • 原文地址:https://blog.csdn.net/m0_73179389/article/details/139697714