02多线程基础

多线程基础

有关Java中多线程的基本使用方式,主要是Thread类的使用。

什么是线程

线程的基本操作

守护线程

线程优先级

基本的线程同步操作

什么是线程

线程是进程的基本单元

线程的基本操作

image-20230304193804401

新建线程

//新建线程
Thread thread = new Thread();
//并启动新线程
thread.start();

//错误启动方式
thread.run();

在新线程中运行某些内容

方式一

Thread thread = new Thread() {
    @Override
    public void run() {
        //...
    }
}
thread.start();

方式二

class XxThread implements Runnable {
    @Override
    public void run() {
        //...
    }
}
Thread thread = new Thread(new XxThread());
thread.start();

线程终止

Thread.stop()不推荐使用。它回会释放所有的monitor,有可能会导致数据的不一致性。

image-20230304195116190

线程中断

public void Thread.interrupt()      //中断线程(实例方法)
public boolean Thread.isInterrupted()   //线程是否被中断(实例方法)
public static boolean Thread.interrupted()  //判断线程是否被中断,并清除当前中断状态(静态方法)

使用线程中断方式停止线程。

//比如原来某个线程中运行的代码是这样的
public void run(){
    while(true) {
        //...具体业务
    }
}

//------------------------------------------
//然后向其中添加判断是否中断的代码
public void run() {
    while(true) {
        //判断是否被中断
        if(Thread.currentThread().isinterrupted()) break;
        
        //...具体业务
    }
}

//在某个位置中断该线程
t1.interrupt();


//------------------------------------------
//如果线程代码中有sleep()类似的代码,sleep允许在睡眠期间被中断,并清中断,需要特别处理一下
//向其中添加判断是否中断的代码
public void run() {
    while(true) {
        //判断是否被中断
        if(Thread.currentThread().isinterrupted()) break;
        
        //...具体业务
        try {
            sleep(1000);
        } catch(InterruptedException e) {
            //从睡眠中被中断业务...
            
            //从睡眠中唤醒(抛出中断异常时,会清中断信号),需重新设置中断状态
            Thread.currentThread().interrupt();
        }
        //...具体业务
    }
}

//在某个位置中断该线程(也可以中断此线程的睡眠状态)
t1.interrupt();

线程的挂起与继续执行

挂起(suspend), 继续执行(resume), 对应两个同名方法。 官方不建议使用。

正常使用一般是先suspend然后resume。如果出现意外情况,先resume后suspend会永久挂起。

image-20230305001613392
image-20230305213602953

suspend方法不会释放锁!!!

如果先加锁,然后resume,再suspend,则死锁发生。

线程谦让yield

public static native void yield(); //静态方法,会把当前线程剩余cpu时间片释放掉,让所有进程重新竞争cpu。

等待线程结束join

public final void join() throws InterruptedException();

pubilc final synchronized void join(long millis) throws InterruptedException();

等待某个线程结束再做事情。

join的实现,实际上是

while(isAlive()) {
    wait(0);
}

Thread.join()文档上有说明。当线程terminated的时候,线程对象的notifyAll方法被调用,唤醒所有等在在此线程上的wait. 因此这里join会被唤醒向下走。 也因此,不建议在线程实例对象上调用wait,notify,notifyAll方法,因为可能会影响到这里。但是其他对象上调用wait,notify,notifyAll是无所谓的。

守护线程

在后台默默完成一些系统工作的服务。主线程退出,守护线程也会随之退出。 垃圾回收线程,jit线程都可以理解为守护线程。

当一个应用内只剩余一个守护线程时,Java虚拟机就会退出。

Thread thread = new Thread();
//线程启动之前就要设置好
thread.setDeamon(true);
thread.start();

eg

image-20230305225521484

线程优先级

多个线程竞争资源的时候,线程优先级会有影响。

eg

public class Main {
    public static void main(String[] args) {
        Thread high = new HighPriority("high");
        Thread low = new LowPriority("low");
        high.setPriority(Thread.MAX_PRIORITY);
        low.setPriority(Thread.MIN_PRIORITY);
        low.start();
        high.start();
    }
}

class HighPriority extends Thread {

    public HighPriority(String name) {
        super(name);
    }

    public static int count = 0;

    @Override
    public void run() {
        while(true) {
            synchronized (Main.class) {
                count++;
                if(count>100000000) {
                    System.out.println("High Priority is completed.");
                    break;
                }
            }
        }
    }

}
class LowPriority extends  Thread {
    public LowPriority(String name) {
        super(name);
    }
    public static int count = 0;

    @Override
    public void run() {
        while(true) {
            synchronized (Main.class) {
                count++;
                if(count>100000000) {
                    System.out.println("Low Priority is completed.");
                    break;
                }
            }
        }
    }
}

基本的线程同步操作

多线程执行过程中如何通信?

线程被挂起,如何被通知?

线程之间有数据竞争,如何来协调竞争?

链接:https://www.nowcoder.com/questionTerminal/26fc16a2a85e49a5bd5fc2b5759dbbc2?

监视器是一种同步机制。 

  java中提供了这种同步机制的实现 

  1、Object类中提供的wait notify notifyall方法 

  2、synchronized关键字隐式锁 或 Lock显示锁 

  3、每个对象自带一个锁 

  4、显示锁的Condition条件,用于协作 

  

  程序员可以使用显示锁或隐式锁实现互斥,wait notify notifyall condition 实现协作

synchronized

  1. 指定加锁对象:给指定对象加锁,进入同步代码前活得给定对象的锁
  2. 直接所用于实例对象方法:相当于给当前实例加锁,进入同步代码前要获取当前实例的锁
  3. 直接作用于静态方法:相当于给当前类加锁,进入同步代码前要获取当前类的锁。

Ojbect.wait和Object.notify

wait和notify方法配合使用

object.wait使用前, 线程要先获取对象object的监视器,比如synchronized(object);

object.wait时, 线程会释放获取的object的监视器。

object.notify使用前,线程也必须拥有对象object的监视器,比如synchronized(object);

object.notfiy时,会通知其他某个wait在此object上的线程。但是此时object.notify并没有释放object上监视器,只有监视器被释放后(比如synchronized(object){… object.notify()….}代码块整体结束后,wait在oject上的其他线程才会获取到oject上的监视器,继续向下执行。

image-20230306222609897

eg1: 加在对象上的锁(监视器)

public class Main {
    //暂不考虑join时被中断的情况
    public static void main(String[] args) throws InterruptedException {
        //这里可以分别使用不同的实例
//        Thread t1 = new Thread(new AccountSync(), "t1");
//        Thread t2 = new Thread(new AccountSync(), "t2");
        //也可以使用相同实例
        AccountSync accountSync = new AccountSync();
        Thread t1 = new Thread(accountSync, "t1");
        Thread t2 = new Thread(accountSync, "t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(AccountSync.count);
    }
}
class AccountSync implements Runnable {
    //监视器/监视对象
    public static Object lock = new Object();

    public static int count = 0;
    @Override
    public void run() {
        for(int i = 0; i < 100000000; i++) {
            synchronized (lock) {
                count++;
            }
        }
    }
}

eg2:加在类上的锁(监视器)或直接加在静态方法上

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new AccountSync2(), "t1");
        Thread t2 = new Thread(new AccountSync2(), "t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(AccountSync2.count);
    }
}

class AccountSync2 implements Runnable {

    public static int count = 0;

    public static synchronized void increase() {
        count++;
    }

    @Override
    public void run() {
        for(int i = 0; i < 100000000; i++ ) {
            increase();
        }
    }
}

eg3: object.wait与object.notify实例

public class Main {
    final static Object object = new Object();

    public static class T1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(System.currentTimeMillis() + ": T1 thread started!");
                try {
                    System.out.println("T1 wait for object.");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + ": T1 thread ended!");
            }
        }
    }

    public static class T2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(System.currentTimeMillis() + ": T2 thread started!");
                object.notify();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
                System.out.println(System.currentTimeMillis() + ": T2 thread ended.");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new T1();
        Thread t2 = new T2();
        t1.start();
        Thread.sleep(1000);
        t2.start();
        t1.join();
        t2.join();
    }
}

Java线程状态与线程通讯wait/notify,park/unpark机制:https://blog.csdn.net/qq_38525526/article/details/90180451

习题

使用wait和notify实现一个线程安全的队列

使用 wait notify 实现一个队列,队列有2个方法,add 和 get 。

add方法往队列中添加元素,get方法往队列中获得元素。队列必须是线程安全的。

如果get执行时,队列为空,线程必须阻塞等待,直到有队列有数据。

如果add时,队列已经满,则add线程要等待,直到队列有空闲空间。

import java.util.LinkedList;
import java.util.List;

/**
 * 使用 wait notify 实现一个队列,队列有2个方法,add 和 get 。
 * add方法往队列中添加元素,get方法往队列中获得元素。队列必须是线程安全的。
 * 如果get执行时,队列为空,线程必须阻塞等待,直到有队列有数据。
 * 如果add时,队列已经满,则add线程要等待,直到队列有空闲空间。
 *
 * 参考:https://maimai.cn/article/detail?fid=1684676370&efid=etOPh0qFntMswKABdHTdGg
 *
 * @param <T> 元素类型
 */
public class SafeQueue<T> {
    public static final int MAX_SIZE = 100;
    private LinkedList<T> list = new LinkedList<>();

    public void add(T ele) {
        synchronized (list) {
            //这里if不合适,因为这里wait可能是被调用add的线程唤醒的
//            if (list.size() >= MAX_SIZE) {
            while (list.size() >= MAX_SIZE) {
                try {
                    list.wait();
                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + ": before add. And current queue size(): " + list.size());
            list.add(ele);
            //唤醒某一个监视此对象的线程(这里notify()不合适, 因为可能唤醒的是add中的wait,有可能造成死锁)
//            list.notify();
            list.notifyAll();
        }
    }

    public T get() {
        T ele = null;
        synchronized (list) {
            //如果队列为空,需要阻塞等待
            //这里if不合适,因为wait可能是被调用get的线程唤醒的
//            if (list.size() == 0) {
            while (list.size() == 0) {
                try {
                    list.wait();
                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + ": before poll. And current queue size(): " + list.size());
            ele = list.poll();
            //唤醒监视此对象的线程(这里notify()不合适,因为有可能唤醒的是调用get的线程,有可能造成死锁)
//            list.notify();
            list.notifyAll();
        }
        return ele;
    }
}

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注