多线程

join()
join就是指等待该线程结束,然后才继续往下执行自身线程,如果该线程已经结束,
对实例调用join()会立刻返回。此外,join(long)的重载方法也可以指定一个等待时间,超过等待时间后就不再继续等待。
如果线程处于等待状态,例如,t.join()会让main线程进入等待状态,此时,如果对main线程调用interrupt(),join()方法会立刻抛出InterruptedException,因此,目标线程只要捕获到join()方法抛出的InterruptedException,就说明有其他线程对其调用了interrupt()方法,通常情况下该线程应该立刻结束运行。

中断线程的两种方法

1.interrupt()方法
目标线程需要反复检测自身状态是否是interrupted状态,如果是,就立刻结束运行。
当有join方法的时候,就捕捉是否有InterruptedException,有的话就结束进程
2.设置标志位

public class Main {
    public static void main(String[] args)  throws InterruptedException {
        HelloThread t = new HelloThread();
        t.start();
        Thread.sleep(1);
        t.running = false; // 标志位置为false
    }
}

class HelloThread extends Thread {
    public volatile boolean running = true;
    public void run() {
        int n = 0;
        while (running) {
            n ++;
            System.out.println(n + " hello!");
        }
        System.out.println("end!");
    }
}

在这里共享变量用volitale修饰保证内存可见性

守护进程

Thread t = new MyThread();
t.setDaemon(true);
t.start();

守护线程是指为其他线程服务的线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。
因此,JVM退出时,不必关心守护线程是否已结束。
同时也需要注意,守护线程不能持有需要关闭的资源(如打开文件等)

线程安全

sychronized
(1)修饰普通方法
(2)修饰静态方法
对静态方法的同步本质上是对类的同步
(3)修饰代码块

sychronized的原理
其实它的本质就是获取监视器锁(monitor)
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

这个过程也能反应出sychronized是个可重入锁。

另外,除了sychronized同步的代码是线程安全的以外,Java标准库的java.lang.StringBuffer也是线程安全的。

还有一些不变类,例如String,Integer,LocalDate,它们的所有成员变量都是final,多线程同时访问时只能读不能写,这些不变类也是线程安全的。后,类似Math这些只提供静态方法,没有成员变量的类,也是线程安全的。
除了上述几种少数情况,大部分类,例如ArrayList,都是非线程安全的类,我们不能在多线程中修改它们。但是,如果所有线程都只读取,不写入,那么ArrayList是可以安全地在线程间共享的。

死锁

因为sychronized是可重入锁,
(JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫做可重入锁。)
所以可能会出现,两个线程各自持有不同的锁,然后各自试图获取对方手里的锁,造成了双方无限等待下去,这就是死锁。
解决办法是,获取锁的顺序必须要一致。

线程池

为什么使用线程池:
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存

callable接口,返回值是future
常用的几种线程池
1.Executors.newCacheThreadPool():可缓存线程池
2.Executors.newFixedThreadPool(int n):创建一个可重用固定个数的线程池
3.Executors.newScheduledThreadPool(int n):创建一个定长线程池,支持定时及周期性任务执行
4 Executors.newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

corePoolSize,线程池大小
maximumPoolSize,任务量忽然增多时,可以达到的最大线程数
largestPoolSize,记录曾经有过的最大线程数目。

https://www.cnblogs.com/jiawen010/p/11855768.html

你可能感兴趣的:(多线程)