首页 > 编程语言 >【Java基础】--线程(二)

【Java基础】--线程(二)

时间:2024-07-06 10:58:10浏览次数:17  
标签:Java synchronized Thread -- void 线程 唤醒 public

5. Thread类中常用的一些方法

5.1 线程休眠方法

public class MyThread extends Thread{
​
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            try {
                //秒杀---
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~"+i);
        }
    }
}

5.2 放弃 yieId方法

yield 当前线程让出cpu-参与下次的竞争

public class MyThread extends Thread{
​
    @Override
    public void run() {
        for (int i = 0; i <20 ; i++) {
            System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~"+i);
            Thread.yield();//当前线程让出cpu参与下次的竞争。
            System.out.println("~~~~~~~~~~~~~~~~");
        }
    }
}

5.3 join加入当前线程上

插入的线程执行完毕后,当前线程才会执行。

​public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1=new MyThread();
        t1.setName("线程A");
        MyThread2 t2=new MyThread2();
        t2.setName("线程B");
        t1.start();
        t2.start();
        t1.join(); //t1加入主线程上,主线程需要等t1执行完毕后才会执行. 如果主线程需要等带t1和t2线程的执行结果 做下一步操作时。
        for (int i = 0; i <20 ; i++) {
            Thread.sleep(10);
            System.out.println("main~~~~~~~~~~~~~~~~~~~~~"+i);
        }
    }
}
​

5.4 setDaemon()

设置线程为守护线程,当所有线程执行完毕后,守护线程也会终止。

//JDK--默认就有一个守护线程.GC垃圾回收。
public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1=new MyThread();
        t1.setName("线程A");
        t1.setDaemon(true);//设置t1为守护线程
        t1.start();
        for (int i = 0; i <20 ; i++) {
            System.out.println("main~~~~~~~~~~~~~~~~~~~~~"+i);
        }
    }
}

6.解决线程安全问题

java程序中如何加锁: synchronized和Lock锁

6.1什么情况下会出现线程安全问题

线程不安全的问题主要出现在多线程环境中,当一个或多个线程在没有适当同步的情况下,同时访问共享资源或数据时,就可能引发线程不安全的问题。这是因为每个线程都有自己的执行路径和速度,它们可能同时读写同一个变量或对象的状态,导致数据的不一致或不可预测的行为。

6.2如何解决线程不安全问题?

      提供了两种方式:第一种:使用synchronized自动锁 第二种: 使用Lock手动锁。使用锁相对于把原来的异步转换为同步操作。 需要注意的是,解决问题的关键在于使用同一个锁对象来确保同步。如果多个线程使用不同的锁对象,则无法解决线程安全问题。

6.2.1同步代码块

格式:synchronized(对象) {
                需要被同步的代码;
            }

    public void run() {
            while (true) {
                synchronized (this){
                    if(ticket > 0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
                        ticket--;
                    }else {
                        break;
                    }
                }
            }
    }

6.2.2同步方法

格式:把同步(synchronized)加在方法上。

 public void run() {
        while (ticket>0) {
            sellTicket();
        }
    }
    public synchronized void sellTicket() {
        if(ticket > 0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张票");
            ticket--;
        }
    }

6.2.3加锁Lock解决问题

Lock它是一个接口,它的实现类。ReentrantLock

public class SellTicket implements Runnable {
    private int tick = 100;

    private Lock l = new ReentrantLock();

    //synchronized使用在方法那么它的共享锁为this
    @Override
    public void run() {
        while (true) {
            try {
                l.lock();//加锁
                if (tick > 0) {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    tick--;
                    System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + tick + "张");
                } else {
                    break;
                }
            } finally {
                l.unlock();//解锁
            }

        }
    }

}

7.死锁

什么是死锁?

线程A拥有锁资源a,希望获取锁资源b. 线程B拥有锁资源b,希望获取锁资源a。 线程A和B拥有对方像获取的锁资源。等待线程---永久等待。---从而造成死锁问题。

线程一

public class Boy extends Thread {
    @Override
    public void run() {
        synchronized (LockObject.lockb) { // 修改为与女孩线程相同的锁获取顺序
            System.out.println("男孩获取锁a");
            synchronized (LockObject.locka) {
                System.out.println("男孩获取锁b");
                System.out.println("男孩开始画画");
            }
        }
    }
}

线程二

public class Girl extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized (LockObject.locka) {
            System.out.println("女孩获取锁a");

            synchronized (LockObject.lockb) {
                System.out.println("女孩获取锁b");
                System.out.println("女孩开始画画");
            }
        }
    }
}

解决办法:

1. 不要使用锁嵌套。
2. 设置超时时间。--Lock类中tryLock.
3. 使用安全java.util.concurrent下的类。

8.线程通信

  1. wait() 方法

    • wait() 方法使当前线程进入等待状态,同时释放它所持有的锁(通过 synchronized 获取的锁),直到其他线程调用相同对象的 notify() 或 notifyAll() 方法唤醒该线程。
    • 在调用 wait() 方法前,线程必须通过获取对象的锁来确保同步。
  2. notify() 方法

    • notify() 方法用于唤醒因调用对象的 wait() 方法而处于等待状态的单个线程。如果有多个线程在等待,那么哪个线程被唤醒是不确定的,取决于 JVM 的实现。
    • 类似地,notifyAll() 方法唤醒所有等待的线程。

代码示例: 

public class BankCard  {
    private double balance;

    private boolean flag;//true:有钱  false:没钱

    public synchronized  void cun(double money){
        if(flag==true){
            try {
                wait();//属于Object类中。 进入等待队列 并且释放拥有的锁
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //+余额
        balance+=money;
        //设置有钱标志
        flag=true;
        //唤醒--唤醒等待队列中的某个线程
        notify();
        System.out.println(Thread.currentThread().getName()+"往卡中存入了:"+money+";卡中余额:"+balance);
    }
    public synchronized void qu(double money){
        if(flag==false){
            try {
                wait(); //放入等待队列中。
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        balance-=money;
        flag=false;
        notify();
        System.out.println(Thread.currentThread().getName()+"从卡中取出了:"+money+";卡中余额:"+balance);
    }
}


public class CunThread extends Thread{
    private BankCard bankCard;

    public CunThread(BankCard bankCard){
        this.bankCard=bankCard;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.cun(1000);
        }
    }
}

package com.ykq.demo06;

public class QuThread extends Thread{


    private BankCard bankCard;

    public QuThread(BankCard bankCard){
        this.bankCard=bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i <10 ; i++) {
            bankCard.qu(1000);
        }
    }
}



  public static void main(String[] args) {
        BankCard bankCard=new BankCard();
        CunThread c=new CunThread(bankCard);
        c.setName("张三");
        QuThread q=new QuThread(bankCard);
        q.setName("李四");
        c.start();
        q.start();
    }

9. 线程状态

        NEW,====新建状态。
        RUNNABLE,===>就绪状态和运行状态
        BLOCKED,===>堵塞状态
        WAITING,====>等待状态
        TIMED_WAITING,===>时间等待
        TERMINATED;===终止。

10. 面试题

1. syn和lock的区别?

syn可以使用代码块和方法。自动加锁和释放锁。不会出现死锁问题。lock它只能使用在代码块中。需要手动加锁和释放锁。如果不释放锁,死锁问题。灵活。它的释放锁必须放finally.


2. 什么是死锁和如何避免死锁。

文中已有答案


3. 线程的状态?

文中已有答案


4. notify和notifyAll的区别。

notify()和notifyAll()都是Java中用于唤醒等待在特定对象上的线程的方法,它们都用于处理多线程编程中的同步问题。然而,它们之间存在一些关键的区别:

用途:notify()通常用于唤醒单个等待在该对象上的线程,而notifyAll()用于唤醒所有等待在该对象上的线程。
阻塞模式:使用notify()唤醒单个线程时,其他线程不会被阻塞,可以继续执行。使用notifyAll()唤醒所有线程时,所有正在等待该对象的线程都会被唤醒,然后它们会尝试重新进入同步代码块或方法。
线程安全性:notifyAll()方法在唤醒所有线程时需要特别小心,因为如果有多个线程同时尝试进入同步代码块或方法,可能会导致数据的不一致性。相比之下,notify()在唤醒单个线程时更加安全。
性能:使用notifyAll()可能会导致更多的线程重新进入同步代码块或方法,这可能会消耗更多的CPU资源。相比之下,使用notify()可以更快地唤醒单个线程,因此可能在某些情况下提供更好的性能。

5. wait和sleep方法。
   1. wait来自于Object。sleep来自于Thread
   2. wait必须放入同步代码块中,sleep可以在任何地方执行。
   3. wait会释放锁资源。sleep不会释放锁。
   4. wait需要手动唤醒。sleep时间到了自动唤醒。


6.Thread类中常用方法。

文中已有答案

还有一点没有写完,明天补上!!!

标签:Java,synchronized,Thread,--,void,线程,唤醒,public
From: https://blog.csdn.net/As_Yua/article/details/140188267

相关文章

  • 毕业设计-基于Springboot+Vue的线上教学平台的设计与实现(源码+LW+包运行)
    源码获取:https://download.csdn.net/download/u011832806/89421458基于SpringBoot+Vue的线上教学平台开发语言:Java数据库:MySQL技术:SpringBoot+MyBatis+Vue.js工具:IDEA/Ecilpse、Navicat、Maven视频演示地址:链接:https://pan.baidu.com/s/1_eN2FDY25D5XUIz4i7Jwcw?pwd=......
  • java08
    面相对象编程对于复杂的实务,为了从宏观上把握,从整体上合理分析,我们需要面向对象的思路,在微观上,我们仍要面向过程编程本质,以类的方式组织代码,以对象组织数据三大特性:封装、继承、多态抽象对象静态方法拥有static的方法,可以直接通过类名来调用,和类一起加载非静态方法没有st......
  • 数组练习题(一)
    1.   (销售人员薪金范围)解决以下问题。一家公司以底薪加提成的方式付给销售人员工资。销售人员每周获得200美元的底薪,外加本周达到一定销售额的9%的提成。例如,一个销售人员一周的销售额是5000美元,就会得到200美元加上5000美元的9%,即总共650美元。请编写一个程序(利用一......
  • 题解:CF1256D Binary String Minimizing
    贪心。数据范围\(n\le10^{6}\),因此我们要用时间复杂度为\(\mathcal{O}\left(n\right)\)的算法来解决这个问题。思路从左至右扫一遍序列,如果遇到\(10\),则要将这个\(0\)交换到前面的位置。由于是字典序最小,\(0\)应该尽量在最高位。现在需要知道这个\(0\)被交换到哪......
  • 工具|--LINQPad|--使用DnSpy调试LINQPad
    前言LINQPad本身就有调试功能,使用dnSpy调试LINQPad的代码,岂不是多此一举?其实主要是为了使用dnSpy调试LINQPad中使用到依赖dll的的底层代码,比如,在LINQPad中使用到了WPF的dll,使用dnSpy就可以调试到WPF的一些底层代码.并且,我尝试过,直接使用dnSpy加载LINQPad......
  • Facebook账户受到限制,我们应该要怎么做
    最近的这段时间Facebook的账户限制越来越多,受到的风控影响到不少企业户封户、无法推广。那么我们要怎么规避这种账户被封,账户受限的情况呢?FB账号被封原因都有哪些当个人号和广告账户被封时,平台有时并不会给出具体原因,大家可以根据下面的内容对照检查,看看哪些地方有问题及时整......
  • 自然语言处理学习--3
    对自然语言处理领域相关文献进行梳理和总结,对学习的文献进行梳理和学习记录。希望和感兴趣的小伙伴们一起学习。欢迎大家在评论区进行学习交流!论文:《ChineseBERT:ChinesePretrainingEnhancedbyGlyphandPinyinInformation》下面将根据以下五部分内容进行论述1.解决了......
  • CLion中文乱码的解决方案
    在网上找了半天CLion中文乱码解决方案,众说纷纭。我就在此说一种最简单,快速,靠谱的方法。目录首先:然后:最后一步:首先:首先按··File->Settings...->Editor->FileEncodings··途径进入然后把-用粗红框框的全改成UTF-8;最后,点击ok退出然后:Ctrl+Shift+Alt+/  进入......
  • Java--Super
    1.super调用父类的构造方法,必须在构造方法的第一个2.super必须只能出现在子类的方法或者构造方法中3.super和this不能同时调用构造该方法和this差别1.代表的对象不同    this():代指本身调用者这个对象    super():代表父类对象的应用2.前提    this......
  • 《IT 领域新生暑期学习之旅》
    IT专业入门,高考假期预习指南七月来临,各省高考分数已揭榜完成。而高考的完结并不意味着学习的结束,而是新旅程的开始。对于有志于踏入IT领域的高考少年们,这个假期是开启探索IT世界的绝佳时机。作为该领域的前行者和经验前辈,你是否愿意为准新生们提供一份全面的学习路线图呢?快来......