8.线程与进程

什么是进程?
进程是程序(应用程序,如:微信)运行资源分配的最小单位

什么是线程?
线程是CPU调度的最小单位,必须依赖于进程存在

CPU时间片轮转机制?
时间片轮转调度是一种最古老、最简单、最公平且使用最广的算法,又称为RR调度.

作用
让每个进程分配一个时间段,称作他(进程)的时间片,即该进程允许运行的时间.(CPU时间切片 )

启动线程有几种方式?

  • new Thread().start();
  • implements Runnable 将mRunnable放在new Thread(mRunnable).start();中去执行

Thread和Runnable之间的区别是什么?
Thread是对线程的抽象
Runnable是对业务/任务的抽象

Java中如何停止线程?
不建议使用stop(停止)方法,因为这个方法执行后没有释放线程所占用的资源,同样的还有suspend(挂起)和resume(唤醒)方法.
应当使用interrupt(中断)方法
下面是两个判断是否被中断的方法:

  • isInterrupted 判断完成后,不会更改interrupt的值,仍是true
  • interrupted 判断完成后,会重置interrupt的值为false
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

public boolean isInterrupted() {
    return this.isInterrupted(false);
}

使用方式:
可以在线程调用interrupt方法后,在Thread的run方法中判断线程是否中断,或者在Runnable的run方法中判断线程是否中断(Runnablerun方法中通过Thread.currentThread()获取当前线程);线程中断后可以做一些资源释放的工作.

JDK中线程是协作式的,而不是抢占式的

深入理解Thread的start方法和run方法

  • start方法调用后,线程的run方法中获取到的线程就是我们当前定义的线程;
  • 不调用start方法,直接调用线程的run方法,那么在run方法中获取到的线程就是main线程

线程的join方法(面试题:如何保证几个线程的顺序执行)
一个线程a启动后,另外一个线程b也启动了,如果正常情况下,应该是a线程执行完成后再执行b线程;但是如果线程b使用join方法,那么线程的执行顺序就会发生变化,线程b执行完成后才会执行线程a.

守护线程Daemmon

  • 可以通过线程的setDaemon(true)方法将当前用户线程更改为守护线程;
  • 如果用户线程执行完成后,守护线程也会随之停止;
  • 并且守护线程的run方法中,try/finally语法中的finally逻辑中的代码不一定会执行.

线程间共享之Synchronized内置锁

  • 同步块 (synchronized(object)参数是对象)
  • 方法 (同样是表示当前类的对象)

对象锁和类锁
对象锁是作用在类的对象身上的,如非静态方法加锁
类锁是作用在当前类加载到虚拟机中class对象上的,如静态方法加锁

volatile关键字
最轻量级的同步机制,保证线程间的可见性;
一写多读的作用,一个线程修改,其他线程都能获取到修改后的值

可见性案例:

如果主线程中将一个变量的值进行更改,在子线程中去打印这个值,这个值不会改变;
如果将这个变量声明为volatile,在主线程中更改他的值,那么在子线程去打印这个值,此时这个变量的值,是更改后的值.

ThreadLocal
每个线程Thread都有一个变量的副本ThreadLocal.ThreadLocalMap,这个map中有一个Entry[]数组,Entry对象是以ThreadLocal作为key,传入的对象作为value的键值对;
我们存入的数据就存放在这个Entry[]数组中,从而实现了多个线程操作同一个ThreadLocal对象的线程安全性,因为每个线程都是在操作当前线程副本 ThreadLocal.ThreadLocalMap中的Entry数组.

说一说ThreadLocal发生内存泄漏?
因为ThreadLocal使用的弱引用WeakReference,所以有GC的时候,当前ThreadLocal对象就会被回收,从而导致了当前Entry对象中keynull,value依旧是之前存入的对象,Entry作为ThreadLocal.ThreadLocalMapEntry[]数组中的一个元素,他持有了该ThreadLocalMap对象,而该ThreadLocalMap对象同时也持有了该Thread线程对象,所以在ThreadLocal被回收时,当前线程仍未被回收,从而导致了内存泄漏.

代码如下:

static class ThreadLocalMap {

    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}

解决方案:
在每次使用完ThreadLocal后,调用threadLocal.remove()方法,将当前ThreadLocal所在的Entry对象从Entry[]数组中移除;代码举例如下:

ThreadLocal<Handler> localVariable = new ThreadLocal<>();
localVariable.set(new Handler());
localVariable.remove();

ThreadLocal线程不安全?
static对象造成的多个线程操作同一个共享对象的引用,从而引发了线程不安全性

线程间的通知和等待,Object类的waitnotify/notifyAll

  • wait wait使用方式,必须放在synchronized同步方法或者代码块中,wait方法代码执行完成后会立即释放锁;等待notify/notifyAll方法将当前wait方法唤醒后,才会重新去竞争锁资源,竞争到锁资源后,才会执行该方法的业务逻辑.
  • notify/notifyAll notify/notifyAll使用方式,必须放在synchronized同步方法或者代码块中,在业务逻辑执行完成后,再调用notify/notifyAll方法,此时将会唤醒wait等待的对象(让wait等待的线程去竞争锁),当该同步方法或者是同步代码块执行完成后才会释放锁.

使用方式

synchronize方法或代码块synchronize(object){
   while(不满足条件){
    对象.wait();
   }
   //满足条件,执行业务逻辑
}

synchronize方法或代码块synchronize(object){
   //执行业务逻辑
   //业务逻辑执行完成后
   对象.notify();//对象.notifyAll();
}

yield(放弃)、sleep(休眠)、wait(等待)、notify(唤醒)比较?

  • yield和sleep是线程Thread的方法,这两个方法在调用后不会释放当前锁占用的资源(需要等待同步方法或者代码块执行完成后才会释放)
  • wait和notify是对象Object的方法,这两个方法在同步方法或同步代码块执行完成后会释放当前同步锁占用的资源

分而治之,Fork-Join(拆分-合并)思想,工作密取,

CountDownLatch闭锁

  • countDown() 扣减 计数器减去1,直至值为0
  • await() 等待 等待计数器是0的时候执行该方法后面的业务逻辑

代码使用

CountDownLatch mCountDownLatch = new CountDownLatch(6);
//多个线程,线程数量小于等于6,一个线程可以多次调用countDown方法,当计数器减至0,会执行await方法后面的业务逻辑
//主线程或者是子线程执行countDown()
mCountDownLatch.countDown();//执行6次,计数器减至0,会执行await方法后面的业务逻辑
//多个线程调用await方法,在计数器减至0时同时执行await方法后面的业务逻辑
mCountDownLatch.await();
//await方法后面的业务逻辑

计数器和线程数不一定是相等的,计数器可以大于等于线程数,线程执行完成一个任务,就调用countDown()方法,计数器减去1;一个线程可以多次执行countDown()方法,此时计数器就减去该方法执行的次数;别的主线程或子线程调用await()方法,当计数器为0的时候,这些等待的线程就会执行await()方法后面的业务逻辑

CyclicBarrier(循环屏障)

  • await 这个方法调用一次计数器加1,计数器达到构造函数中的int值时,执行await方法后面的业务逻辑和汇总线程中的业务逻辑

使用方式:

//在await调用次数为计数器4的时候,会第一次执行汇总线程CollectThread()中的业务逻辑,同时接着会执行await方法后面的业务逻辑;
//await调用次数为计数器8的时候,会第二次执行汇总线程CollectThread()中的业务逻辑,同时接着会执行await方法后面的业务逻辑,
//这就是CyclicBarrier中Cyclic循环的含义
CyclicBarrier mCyclicBarrier = new CyclicBarrier(4, new CollectThread());
mCyclicBarrier.await();
//TODO await方法后面的业务逻辑

相较于CountDownLatch可以循环使用,CountDownLatch在计数器为0的时候,可以执行主线程或者子线程中await方法后的业务逻辑,但是只能执行1次;而CyclicBarrier在调用await方法后,在执行次数等于计数器值的时候,各个线程同样也会执行await方法后面的业务逻辑;同时该API还可以定义一个汇总线程,用于处理等待的计数器值达到临界值时,在汇总线程中处理业务逻辑.
await方法达到计数器临界值时,计数器的值会进行重置,如果在后面重复调用await方法,可以第2次达到计数器临界值,循环执行汇总线程或者await方法后面的业务逻辑.

Callable、Futrue和FutureTask

UseCallable useCallable = new UseCallable();
//用FutureTask包装Callable
FutureTask<Integer> futureTask = new FutureTask<>(useCallable);
//交给Thread去运行
new Thread(futureTask).start();
//futureTask拿到传入参数Callable(userCallable)的执行结果
futureTask.get();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值