java多线程

为什么需要多线程?

打开任务管理器,我们会看到计算机里面有一个个的进程,进程进一步的划分也就是线程了。我们运行java程序实际上就是运行了一个jvm进程,jvm默认会用主线程来执行main方法,另外jvm里面还有负责垃圾回收的其他工作线程等等,java语言做了多线程的支持,也就是说我们可以使用多线程实现多任务。
现代计算机的cpu多采用多核架构,为了充分利用性能以及让耗时操作(读写文件等等)更有效率,所以在特定场合(高并发)需要多线程操作。

线程的生命周期

在Java程序中,一个线程对象只能调用一次start()方法启动新线程,并在新线程中执行run()方法。一旦run()方法执行完毕,线程就结束了。因此,Java线程的状态有以下几种:
New:新创建的线程,尚未执行;
Runnable:运行中的线程,正在执行run()方法的Java代码;
Blocked:运行中的线程,因为某些操作被阻塞而挂起;
Waiting:运行中的线程,因为某些操作在等待中;
Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待;
Terminated:线程已终止,因为run()方法执行完毕。


image.png

线程安全问题

当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。

这个时候,有个单线程模型下不存在的问题就来了:如果多个线程同时读写共享变量,会出现数据不一致的问题。
多线程模型下,要保证逻辑正确,对共享变量进行读写时,必须保证一组指令以原子方式执行:即某一个线程执行时,其他线程必须等待。通常我们会通过加锁和解锁的方式来确保执行顺讯。

但是加锁的过程中如果出现使用不当的情况,就会出现死锁。

public void add(int m) {
    synchronized(lockA) { // 获得lockA的锁
        this.value += m;
        synchronized(lockB) { // 获得lockB的锁
            this.another += m;
        } // 释放lockB的锁
    } // 释放lockA的锁
}

public void dec(int m) {
    synchronized(lockB) { // 获得lockB的锁
        this.another -= m;
        synchronized(lockA) { // 获得lockA的锁
            this.value -= m;
        } // 释放lockA的锁
    } // 释放lockB的锁
}

如果两个线程去执行上面两个方法就会出现死锁。那么我们应该如何避免死锁呢?答案是:线程获取锁的顺序要一致。

线程池

Java语言虽然内置了多线程支持,启动一个新线程非常方便,但是,创建线程需要操作系统资源(线程资源,栈空间等),频繁创建和销毁大量线程需要消耗大量时间。
那么我们就可以把很多小任务让一组线程来执行,而不是一个任务对应一个新线程。这种能接收大量小任务并进行分发处理的就是线程池。

简单地说,线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待状态。如果有新任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,新任务要么放入队列等待,要么增加一个新线程进行处理。

threadLocal

顾名思义,指的就是线程里面的储存空间,在当前线程能一直获取同一个实例。实际上,可以把ThreadLocal看成一个全局Map:每个线程获取ThreadLocal变量时,总是使用Thread自身作为key。

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