美文网首页
Java 多线程

Java 多线程

作者: X小飞侠 | 来源:发表于2018-03-29 11:43 被阅读23次

1 简述

1.1 线程机制

Java使用的是抢占式的线程机制,调度机制周期性地切换上下文,切换线程,从而为每个线程提供时间片。看似同时执行,其实是不停地切换。在这种机制下,一个线程的阻塞不会导致整个进程阻塞。

优先级较低的线程仅仅是执行的频率较低,不会得不到执行。

要实现线程行为,你必须显示地将一个任务(Runnable)附着到线程(Thread)上。Thread类只是驱动赋予它的任务Runnable。

1.2 阻塞(se第四声)

程序中一个任务因为该程序控制范围外的因素(条件通常是IO),而不能继续执行,这个任务线程被阻塞了。

2 Executor 线程池

用new Thread()的方式不利于性能优化,所以采用线程池替代,达到线程的复用。首选CachedThreadPool回收旧线程停止创建新线程。

ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Runnable() {
        @Override
        public void run() {

        }
});

Runnable设返回值,需要实现Callable接口call()方法,并且必须使用ExecutorService.submit()方法调用。
使用 Future.get() 获取返回值。
例如:

public class CallableAndFuture {
    static class MyThread implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Hello world";
        }
    }

    static class MyThread2 implements Runnable {
        @Override
        public void run() {

        }
    }

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Future<String> future = threadPool.submit(new MyThread());

        try {
            System.out.println(future.get());
        } catch (Exception e) {

        } finally {
            threadPool.shutdown();
        }
    }
}

3 线程安全

3.1 实质

线程安全其实就是解决共享资源竞争的问题。
解决方法是:当一个资源被一个任务使用时,加锁。

解锁的时候,下一个要使用资源的任务并没有按照排队的方式按次序来获取,而是通过竞争获取资源。可以通过yield()和setPriority()来给线程调度器提供建议,这效果取决于具体平台和JVM实现。

3.2 synchronized加锁

对于某个特定对象,其所有的synchronized方法共享同一个锁。
一个任务可以多次获得对象的锁。

判断是否应该加锁:如果你正在写一个变量,这个变量接下来将被另一个线程读取;你正在读一个上一次已经被另一个线程写过的变量。以上两种情况都必须使用同步。并且,读写线程都必须用相同的监视器锁同步。

3.3 使用显式的Lock对象加锁

除了synchronized方式加锁之外,还可以用Lock对象:

private Lock lock = new ReentrantLock();//重入锁
......
lock.lock();
try{
......
return ...;//在try中return确保unlock()不会过早发生
}finally{
lock.unlock();
}

和synchronized的区别:tryLock()

  1. 可以尝试着获取锁,最终获取失败。
  2. 可以尝试着获取锁一段时间,然后放弃获取。

如果其他的线程已经获取了这个锁,你可以决定离开,去执行其他一些事,而不是等待直至这个锁释放。提供了比Synchronized更细粒度的控制力。(例如可以释放当前锁之前,捕获下一节点的锁)

4 volatile

volatile关键字主要提供2种作用:

  1. 可见性:当使用volatile关键字去修饰变量的时候,所有线程都会直接读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的
  2. 禁止指令重排序:应用在标志位

5线程本地存储

防止共享资源产生冲突的第二种方式是根除变量的共享,使用ThreadLocal。

6 终结任务

线程的各个状态


线程的各个状态

停止线程主要使用标志位,在run()方法中加以判断,是否要执行。

  1. 自己定义一个volatile boolean的标志位
  2. 使用interrupt()方法,但是这个方法也是打个停止的标志,并不是真正的停止线程,还是要在run()方法中调用Thread.interrupted()方法判断。

7 线程之间的协作

yield()方法:调用的线程放弃cpu时间让给别的线程。
join()方法:调用的线程先执行。

7.1 wait()

wait()是object的方法,它会释放锁,而sleep()和yield()不会释放。
因此在该对象(未锁定的)中的其他synchronized方法可以在wait()期间被调用。

只能在同步控制方法或者同步控制块里调用wait()、notify()、notifyAll(),因为要操作对象的锁。

wait()必须用一个检查感兴趣的条件的while循环包围,本质是检查感兴趣的条件,并在条件不满足的情况下返回到wait()方法中。

Thread1:
synchronized(sharedMonitor){
  <setup condition for Thread2>
  sharedMonitor.notify();
}
//错误的使用:时机太晚,错失信号,盲目进入wait(),产生死锁。
Thread2:
while(someConditon){
  synchronized(sharedMonitor){
    sharedMonitor.wait();
  }
}
//正确的使用:防止在someCondition变量上产生竞争条件
Thread2:
 synchronized(sharedMonitor){
    while(someConditon){
         sharedMonitor.wait();
    }
 }

7.2 notify()和notifyAll()

notify():在众多等待同一个锁的线程中,只有一个会被唤醒去获取锁。
notifyAll():在众多等待同一个锁的线程中,全部会被唤醒去共同竞争锁。
两个同样最终只有一个线程能获取到锁。

可能有多个线程某单个对象上处于wait状态,因此调用notifyAll()比notify()更安全。
只有一个线程实际处于wait()状态,这时可以用notify()代替notifyAll()优化;

如果有多个线程在等待不同条件,使用notify()就不能知道是否唤醒了恰当的任务。

8 死锁

某个线程在等待另一个线程,而后者又在等待别的线程,这样一直下去,直到这个链上的线程又在等待第一个线程释放锁。这得到了一个状态:线程之间相互等待的连续循环,没有哪个线程能继续。这被称为死锁

死锁的4个条件:

  1. 互斥条件(资源的特点):线程使用的资源中至少有一个是不能共享的。
  2. 占用请求条件(单个线程的特点):至少有一个线程,它持有一个资源,并且等待获取另一个当前被别的线程持有的资源。
  3. 不可剥夺条件(单个线程的特点):已持有的资源不能被别的线程抢占,而它(持有资源的线程)自己也不会主动去抢别的线程的资源。
  4. 循环等待条件(多个线程形成的特点):必须有循环等待。

解决:让一方先放弃资源作出让步。

相关文章

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • 带你搞懂Java多线程(六)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四)带...

  • Java多线程目录

    Java多线程目录 Java多线程1 线程基础Java多线程2 多个线程之间共享数据Java多线程3 原子性操作类...

  • java多线程--Callable

    **移步[java多线程系列文章]Java多线程(二十二)---LockSupport工具Java 停止线程 一、...

  • android 多线程 — 线程的面试题和答案

    这里都是我从各个地方找来的资料,鸣谢: Java多线程干货系列—(一)Java多线程基础 JAVA多线程和并发基础...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • 带你搞懂Java多线程(四)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三) 什么是线程间的协作 线程之间...

  • Java基础(六)

    多线程 Java多线程并发 1.1 JAVA 并发知识库 1.2 JAVA 线程实现/创建方式 1.2.1 继承 ...

  • (五) volatile关键字

    Java多线程目录 1 背景 理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为...

  • Java多线程高级特性(JDK8)

    [TOC] 一、Java多线程 1.Java多线程基础知识 Java 给多线程编程提供了内置的支持。一条线程指的是...

网友评论

      本文标题:Java 多线程

      本文链接:https://www.haomeiwen.com/subject/tprucftx.html