乐趣区

关于java:2020多线程笔记总结

JAVA 线程实现 / 创立形式

继承 Thread 类

Thread 类实质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的惟一办法就是通过 Thread 类的 start()实例办法。start()办法是一个 native 办法,它将启动一个新线程,并执行 run()办法。

public class MyThread extends Thread {public void run() {System.out.println("MyThread.run()");
    }
}
MyThread myThread1 = new MyThread();
myThread1.start();
实现 Runnable 接口

如果本人的类曾经 extends 另一个类,就无奈间接 extends Thread,此时,能够实现一个 Runnable 接口。

public class MyThread extends OtherClass implements Runnable {public void run() {System.out.println("MyThread.run()");
    }
}
实现 callbale

有返回值的工作必须实现 Callable 接口,相似的,无返回值的工作必须 Runnable 接口。执行 Callable 工作后,能够获取一个 Future 的对象,在该对象上调用 get 就能够获取到 Callable 工作返回的 Object 了。

public class callableTest {public static void main(String[] args) throws InterruptedException, ExecutionException {ExecutorService executor = Executors.newFixedThreadPool(10);
        // 创立一个 Callable,3 秒后返回 String 类型
        Callable myCallable = new Callable() {
            @Override
            public String call() throws Exception {//                Thread.sleep(3000);
                System.out.println("calld 办法执行了");
                return "call 办法返回值";
            }
        };
        List<Future> list = new ArrayList<Future>();
        for (int i = 0; i < 10; i++) {Future future = executor.submit(myCallable);
            list.add(future);
        }

线程的状态

线程生命周期

当线程被创立并启动当前,它既不是一启动就进入了执行状态,也不是始终处于执行状态。
在线程的生命周期中,它要通过新建 (New)、就绪(Runnable)、运行(Running)、阻塞(Blocked) 和死亡(Dead)5 种状态。尤其是当线程启动当前,它不可能始终 ” 霸占 ” 着 CPU 单独运行,所以 CPU 须要在多条线程之间切换,于是线程状态也会屡次在运行、阻塞之间切换。

新建状态(NEW)

当程序应用 new 关键字创立了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配内存,并初始化其成员变量的值。

就绪状态(RUNNABLE)

当线程对象调用了 start()办法之后,该线程处于就绪状态。Java 虚构机会为其创立办法调用栈和程序计数器,期待调度运行。

运行状态(RUNNING)

如果处于就绪状态的线程取得了 CPU,开始执行 run()办法的线程执行体,则该线程处于运行状态。

阻塞状态(BLOCKED)

阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,临时进行运行。

直到线程进入可运行 (runnable) 状态,才有机会再次取得 cpu timeslice 转到运行 (running) 状态。阻塞的状况分三种:

期待阻塞(o.wait-> 期待对列):

运行 (running) 的线程执行 o.wait()办法,JVM 会把该线程放入期待队列 (waitting queue) 中。

同步阻塞(lock-> 锁池)

运行 (running) 的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池 (lock pool) 中。

其余阻塞(sleep/join)

运行 (running) 的线程执行 Thread.sleep(long ms)或 t.join()办法,或者收回了 I/O 申请时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()期待线程终止或者超时、或者 I/ O 处理完毕时,线程从新转入可运行 (runnable) 状态。

线程死亡(DEAD)

线程会以上面三种形式完结,完结后就是死亡状态。

1. 失常完结
run() 或 call()办法执行实现,线程失常完结。

2. 异样完结
线程抛出一个未捕捉的 Exception 或 Error。

3. 调用 stop
间接调用该线程的 stop()办法来完结该线程—该办法通常容易导致死锁,不举荐应用。

锁的降级

如果有一个对象在被多个线程同时竞争,那么判断对象是否有锁,如果有锁,那么会先反对偏差锁,就是说以后曾经取得锁的线程会优先拿到锁(markword 区记录了偏差线程 id)。那么拿不到锁的线程,就会降级锁,变成 CAS synchronized 乐观锁,会进行一段时间的循环自旋一直尝试获取锁,当自旋到肯定次数后,会再次升级成 synchronized 重量级锁。

synchronized

锁办法会锁住 this, 锁静态方法会锁住 class 对象. 锁代码块能够指定任意对象作为锁.

同步代码块可能会波及到一个重入过程,synchronized 不会说因为重入去一直反复获取锁开释锁的过程,而是用 mointer 每次重入去做一个计数器加一操作,在开释锁的过程中也会逐渐将计算器清零。而后让其余线程从 block 阻塞状态变成 runnable 状态去竞争这个锁。

synchronized 和 reentranLock 的区别

synchronized 不必手动编程,他是一个 jvm 关键字,我也不必关怀他锁开释的一个过程,间接用就行了,而 reentrantlock 他是一个类,须要手动 lock, 配合 try catch finally 中去做一个锁开释操作

线程池

线程和数据库连贯这些资源都是十分贵重的资源。那么每次须要的时候创立,不须要的时候销毁,是十分浪费资源的。Java 外面线程池的顶级接口是 Executor,然而严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是 ExecutorService。

一个线程池建设后,如果没有预加载工作,他一开始的外围线程数为 0,当一个新工作被提交时,会建设一个外围线程去执行工作,如果始终来工作,而先前建设的外围线程都在忙,那么就会始终建设外围线程直到达到最大外围线程数。

但外围线程数最大,而且都在执行工作时,起初的工作会被放到 blockingqueue(阻塞队列里),如果阻塞队列也满了,就会去建设新线程,此时的线程叫非核心线程,当整个线程池的线程数达到最大,他也有一个 max access 时,会触发回绝策略。

回绝策略

AbortPolicy 停止策略

会间接抛出异样来执行停止工作执行回绝

DiscardPolicy 摈弃策略

他会抛弃不执行多余的工作来执行回绝

DIscardOldestPolicy

会抛弃最早你未执行的工作

callrunpolicy

偏心锁和非偏心锁的区别

在多线程环境下

  1. 偏心锁个别指代 先达到临界区的线程肯定比后到临界区的线程 优先拿到锁
  2. 非偏心锁则指代 先达到临界区的线程也不肯定比后到临界区的线程 优先拿到锁

退出移动版