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

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类中常用方法。

文中已有答案

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

  • 37
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值