多线程的一些理解与归纳(二)

1.线程让步

线程让步,通过yield()函数使得当前线程从运行态–>就绪态。这里要注意的是当前线程回归就绪态后会重新竞争cpu资源,有可能直接继续进入运行态。

2.join()

让“主线程”等待“子线程”结束之后才能继续运行。

3.线程中断

interrupt()的作用是中断本线程。
本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
中断一个“已终止的线程”不会产生任何操作。

// 使用interrupt()中断阻塞状态线程示例

class MyThread extends Thread {
    
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        try {  
            int i=0;
            while (!isInterrupted()) {
                Thread.sleep(100); // 休眠100ms
                i++;
                System.out.println(Thread.currentThread().getName()+" ("+this.getState()+") loop " + i);  
            }
        } catch (InterruptedException e) {  
            System.out.println(Thread.currentThread().getName() +" ("+this.getState()+") catch InterruptedException.");  
        }
    }
}

public class Demo1 {

    public static void main(String[] args) {  
        try {  
            Thread t1 = new MyThread("t1");  // 新建“线程t1”
            System.out.println(t1.getName() +" ("+t1.getState()+") is new.");  

            t1.start();                      // 启动“线程t1”
            System.out.println(t1.getName() +" ("+t1.getState()+") is started.");  

            // 主线程休眠300ms,然后主线程给t1发“中断”指令。
            Thread.sleep(300);
            t1.interrupt();
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted.");

            // 主线程休眠300ms,然后查看t1的状态。
            Thread.sleep(300);
            System.out.println(t1.getName() +" ("+t1.getState()+") is interrupted now.");
        } catch (InterruptedException e) {  
            e.printStackTrace();
        }
    } 
}

最后谈谈 interrupted() 和 isInterrupted()。
interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。
区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。

5.线程优先级

java 中的线程优先级的范围是1~10,默认的优先级是5。“高优先级线程”会优先于“低优先级线程”执行。

java 中有两种线程:用户线程和守护线程。可以通过isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。
用户线程一般用户执行用户级任务,而守护线程也就是“后台线程”,一般用来执行后台任务。需要注意的是:Java虚拟机在“用户线程”都结束后会后退出。

6.生产者和消费者问题

// Demo1.java
// 仓库
class Depot {
    private int capacity;    // 仓库的容量
    private int size;        // 仓库的实际数量

    public Depot(int capacity) {
        this.capacity = capacity;
        this.size = 0;
    }

    public synchronized void produce(int val) {
        try {
             // left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
            int left = val;
            while (left > 0) {
                // 库存已满时,等待“消费者”消费产品。
                while (size >= capacity)
                    wait();
                // 获取“实际生产的数量”(即库存中新增的数量)
                // 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
                // 否则“实际增量”=“想要生产的数量”
                int inc = (size+left)>capacity ? (capacity-size) : left;
                size += inc;
                left -= inc;
                System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", 
                        Thread.currentThread().getName(), val, left, inc, size);
                // 通知“消费者”可以消费了。
                notifyAll();
            }
        } catch (InterruptedException e) {
        }
    } 

    public synchronized void consume(int val) {
        try {
            // left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
            int left = val;
            while (left > 0) {
                // 库存为0时,等待“生产者”生产产品。
                while (size <= 0)
                    wait();
                // 获取“实际消费的数量”(即库存中实际减少的数量)
                // 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
                // 否则,“实际消费量”=“客户要消费的数量”。
                int dec = (size

说明:
(01) Producer是“生产者”类,它与“仓库(depot)”关联。当调用“生产者”的produce()方法时,它会新建一个线程并向“仓库”中生产产品。
(02) Customer是“消费者”类,它与“仓库(depot)”关联。当调用“消费者”的consume()方法时,它会新建一个线程并消费“仓库”中的产品。
(03) Depot是“仓库”类,仓库中记录“仓库的容量(capacity)”以及“仓库中当前产品数目(size)”。
“仓库”类的生产方法produce()和消费方法consume()方法都是synchronized方法,进入synchronized方法体,意味着这个线程获取到了该“仓库”对象的同步锁。这也就是说,同一时间,生产者和消费者线程只能有一个能运行。通过同步锁,实现了对“残酷”的互斥访问。
对于生产方法produce()而言:当仓库满时,生产者线程等待,需要等待消费者消费产品之后,生产线程才能生产;生产者线程生产完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“消费者线程”,即我们所说的“通知消费者进行消费”。
对于消费方法consume()而言:当仓库为空时,消费者线程等待,需要等待生产者生产产品之后,消费者线程才能消费;消费者线程消费完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“生产者线程”,即我们所说的“通知生产者进行生产”。

你可能感兴趣的:(多线程的一些理解与归纳(二))