“Java线程深入解析:常用方法、线程安全、通信与状态管理“

1 Thread中的常用方法

1.1 线程休眠方法—Sleep

在Java中,Thread.sleep()方法用于使当前线程暂停执行指定的毫秒数。这是一个静态方法,它可以让当前执行的线程暂停一段时间,这段时间内该线程不会执行任何代码,也不会消耗CPU资源。

使用Thread.sleep()时需要注意几点:

  1. sleep()方法可能会抛出InterruptedException
  2. sleep()方法不会释放任何锁资源,如果线程在持有某个对象的监视器锁(即,该线程在某个synchronized块或方法中)时调用了sleep()方法,那么其他试图进入该synchronized块或方法的线程将会被阻塞,直到第一个线程醒来并退出synchronized块或方法。
  3. sleep()只是一个简单的暂停,当线程醒来后,它将从暂停的地方继续执行。

下面是一个简单的示例代码,展示了如何使用Thread.sleep()方法:

public class SleepExample {  
    public static void main(String[] args) {  
        try {  
            System.out.println("线程开始休眠...");  
            // 使当前线程休眠3秒钟  
            Thread.sleep(3000);  
            System.out.println("线程休眠结束!");  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}

在这个例子中,主线程会打印一条消息,然后休眠3秒钟,之后再打印另一条消息。

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

当然,下面是一个简单的代码示例,用于解释Thread.yield()方法的使用及其潜在效果:

public class YieldExample {  
    public static void main(String[] args) {  
        // 创建两个线程  
        Thread thread1 = new Thread(() -> {  
            for (int i = 0; i < 5; i++) {  
                System.out.println("线程1正在运行: " + i);  
                Thread.yield(); // 线程1让出CPU执行权  
            }  
        });  
  
        Thread thread2 = new Thread(() -> {  
            for (int i = 0; i < 5; i++) {  
                System.out.println("线程2正在运行: " + i);  
                // 线程2没有调用yield(),因此会持续运行直到时间片用完或被更高优先级的线程抢占  
            }  
        });  
  
        // 启动两个线程  
        thread1.start();  
        thread2.start();  
    }  
}

在这个例子中,我们创建了两个线程thread1thread2thread1在每次循环中都会调用Thread.yield()方法,表示它愿意在每次打印之后让出CPU执行权给其他线程。而thread2则不会主动让出CPU执行权。

1.3 join加入当前线程
当在A线程中调用了B线程的`join()`方法时,A线程会等待B线程执行完毕后再继续执行。如果`join()`方法中传入了参数,如`join(10)`,则表示A线程会等待B线程执行10毫秒,超时后A、B线程将并行执行。值得注意的是,`join(0)`并不意味着A线程等待B线程0秒,而是表示A线程会无限期地等待B线程执行完毕。
1.4 setDaemon()设置线程为守护线程

守护线程是一种在后台运行的线程,其目的是为其他线程(通常是用户线程)提供服务。当程序中没有用户线程在运行时,所有的守护线程都会被自动终止,这意味着如果只剩下守护线程,那么Java虚拟机就会退出。

在Java中,setDaemon(boolean on) 方法用于将一个线程设置为守护线程(daemon thread),这个方法必须在调用线程的 start() 方法之前被调用。

2 线程安全问题

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

当多个线程操作同一个资源时,则出现线程安全问题。

2.2 java如何解决线程安全问题
synchronized自动锁
  • synchronized可以用在方法和代码块上。当修饰方法时,称为同步方法;修饰代码块时,称为同步块。

  • 优点:是Java内置的关键字,使用方便,系统会自动释放锁,无需手动管理。

  • 缺点:不够灵活,无法判断锁的状态,且在竞争激烈的情况下性能可能不如Lock。

    同步方法:直接在方法声明上添加synchronized关键字。

    public synchronized void synchronizedMethod() {  
        // 同步代码块  
    }
    

    同步块:在需要同步的代码块前使用synchronized关键字,并指定一个对象作为锁。

    public void someMethod() {  
        synchronized (this) {  
            // 同步代码块  
        }  
    }
    
Lock手动锁

创建Lock对象:通常使用ReentrantLock类的实例。


Lock lock = new ReentrantLock();

获取锁:在需要同步的代码块前调用lock()方法获取锁。

lock.lock(); // 获取锁  
try {  
    // 同步代码块  
} finally {  
    lock.unlock(); // 释放锁  
}

释放锁:在finally块中调用unlock()方法释放锁,以确保锁一定会被释放。

Lock接口还提供了其他功能,如:

  • 尝试获取锁tryLock()方法尝试获取锁,如果获取成功则返回true,否则返回false。
if (lock.tryLock()) {  
    try {  
        // 同步代码块  
    } finally {  
        lock.unlock();  
    }  
}
  • 超时获取锁tryLock(long timeout, TimeUnit unit)方法尝试在给定的时间内获取锁。
if (lock.tryLock(10, TimeUnit.SECONDS)) {  
    try {  
        // 同步代码块  
    } finally {  
        lock.unlock();  
    }  
}

3 死锁

线程A拥有锁资源a,希望获取锁资源b,线程B拥有锁资源b,希望获取锁资源a。 两个线程互相拥有对方希望获取的锁资源。可能会出现程序堵塞。从而造成死锁。

解决:

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

4 线程通信

wait()方法使当前线程进入等待状态,并且释放当前线程持有的锁,这样其他线程就可以获得该锁并执行相应操作。

notify()方法用于唤醒等待在当前对象上的一个线程(随机选择),而notifyAll()方法则用于唤醒所有等待在当前对象上的线程。

上述方法必须在同步方法或同步块中调用

举例:有一张公共银行卡,确保在线程A存钱之后,线程B在取钱。

银行卡

package com.ykq.demo06;
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);
}
}


线程A存钱:

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);
    }
}
}

线程B取钱:

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("线程A");
        QuThread q=new QuThread(bankCard);
        q.setName("线程B");
        c.start();
        q.start();
    }

7. 线程状态

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

在这里插入图片描述

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值