Java多线程学习总结

来源:互联网 发布:程序员月薪5万 编辑:程序博客网 时间:2024/06/05 22:59

Java多线程学习总结

  1. 线程的概念
  2. 线程的创建
  3. 线程的操作
  4. 多线程(对象锁、信号、控制)

1.线程的概念

讨论线程我们需要明白四个概念:

线程:CPU进行资源调度的最小单位,与进程中其他线程共享资源;

多线程:指的是这个程序(一个进程)运行时产生了不止一个线程;

并行:同一时刻同时执行;

并发:通过cpu调度算法,表现为一段时间上的一起执行;

1.1.线程的状态

新建状态(new)
就绪状态(Runnable)
运行状态(Running)
阻塞状态(Blocked)
死亡状态(Dead)

状态转换图

2.线程的创建

Java中线程的创建主要分为三种方式:常见的两种为继承Thread类、实现Runnable接口,除此之外,使用ExecutorService、Callable、Future实现有返回结果的多线程(线程池相关知识);

2.1.继承Thread类

通过继承Thread类,重新实现run函数,完成线程的创建

public class TestThread extends Thread{    @Override    public void run(){        while(true){            TestUnit.print();            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }}

2.2.实现Runnable接口

任何一个类实现了Runnable接口都可以看成线程的执行单元,可以通过Thread对象或线程池对象来完成对其的控制;

Thread t1 = new Thread(new Runnable(){    @Override    public void run() {        // TODO Auto-generated method stub        while(true){            TestUnit.print();            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }    }});

2.3.使用ExecutorService、Callable、Future实现

实现Callable接口的call方法后,可作为线程池(ExecutorService)的执行单元,注册执行;Future用于获取call方法执行结果;

//TestCallable.javapackage com.aaron.concurrent.test;import java.util.concurrent.Callable;public class TestCallable implements Callable<Object> {    private int taskNum = -1;    public TestCallable(int i) {        this.taskNum = i;    }    public TestCallable() {        // TODO Auto-generated constructor stub    }    @Override    public Object call() throws Exception {        if(this.taskNum != -1){            return "My number is : "+ this.taskNum;        }        else{            return " I have no number!";        }    }}//MainCallable.javapackage com.aaron.concurrent.test;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.TimeUnit;public class MainCallable {    public static final int TASK_NUM = 5;    public static final int MAX_TIME_OUT = 2;    public static void main(String[] args) throws InterruptedException, ExecutionException {        ExecutorService pool = Executors.newFixedThreadPool(TASK_NUM);         List<Future> returnList = new ArrayList<Future>();          for (int i = 0; i < TASK_NUM; i++) {              Callable c = new TestCallable(i);              Future f = pool.submit(c);               returnList.add(f);          }         // 关闭线程池 ,等待线程执行完毕        pool.awaitTermination(MAX_TIME_OUT, TimeUnit.SECONDS);        pool.shutdown();         for (Future f : returnList) {              // 从Future对象上获取任务的返回值,并输出到控制台              System.out.println(f.get().toString());          }      }}

3.线程的操作

3.1.启动线程:start()

start() 方法来启动线程,线程进入就绪状态,并不是马上执行run函数中的代码;

3.2.等待线程执行结束:join()

在代码中调用ThreadA.join(),会等待ThreadA执行完毕后才会执行join之后的代码

3.2.等待线程执行结束:join()

在代码中调用ThreadA.join(),会等待ThreadA执行完毕后才会执行join之后的代码

join方法

3.3.暂停:休眠(sleep)和等待(wait)

sleep和wait方法都能是线程进入阻塞状态,不过还是有不同之处,sleep方法不会交出线程的同步锁(monitor),依然参加时间片的轮转,只是不执行sleep以后的代码,同时sleep属于静态方法,只对调用它的线程有效,如Thread.sleep(100);而wait方法会释放同步锁,线程进入等待池中,不占用cpu,只用等到执行notify操作,才会唤醒该线程进入就绪状态;

这里写图片描述

3.3.让步:yield

yield函数使得线程放弃cpu使用权,从运行状态进入就绪状态,由系统进行重新调度,在实际使用中可能会没有效果,如只有一个线程;

yield

3.4.唤醒:nitify和notifyAll

nitify函数可以唤醒正在共享对象等待池中处于阻塞状态的线程,重新进入到就绪队列,nitifyAll则是唤醒所有的阻塞线程;如果等待池中线程为空或者依旧无法获取到同步锁,则没有效果;

3.5.伪中断:interrupt

interrupt()函数只会修改线程运行的状态,并不会直接关闭线程;并通过异常的方式,提供程序员处理线程关闭的后续工作;如果线程本身处于阻塞状态(sleep/join/wait、io)会直接抛出InterruptedException 异常;否则,除非主动检查线程运行状态,interrupt()将不会有效果,也不会抛出异常;

4. 多线程(对象锁、信号、控制)

4.1.关键字(synchronized)

synchronized关键字有三个关键的问题:
1.哪里能使用
2.锁住的内容
3.锁住的对象

Java多线程同步Synchronized使用分析

4.2.锁机制(Lock接口)

synchronized关键字锁的获取和释放由硬件底层来完成,简单却不灵活,而Java提供的一些锁机制由程序员来来控制,比synchronized要灵活很多;

可以说锁是各种读写控制策略的抽象,程序员可以了解并使用Java提供的读写锁,但是也能根据具体的场景扩展读写规则;

常用的Java读写锁:重入锁(ReentrantLock)、ReentrantReadWriteLock(读写锁)

//--Lock接口public interface Lock {    void lock();    void lockInterruptibly() throws InterruptedException;    boolean tryLock();    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;    void unlock();    Condition newCondition();}//--ReadWriteLock接口public interface ReadWriteLock {    /**     * Returns the lock used for reading.     *     * @return the lock used for reading     */    Lock readLock();    /**     * Returns the lock used for writing.     *     * @return the lock used for writing     */    Lock writeLock();}

锁相关的概念(和锁机制有关):可重入锁、可中断锁、公平锁、读写锁

java多线程和并发 by人生设计师

4.3.多线程相关概念

4.3.1.死锁

锁没有释放或者同时请求同一个资源,解决一般的思路是锁的资源尽可能少,对资源的访问尽量是有序的;

4.3.1.线程安全和线程安全的集合类

当多个线程访问一个对象,如果不考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象是线程安全的;

Java提供一些常用的线程安全集合类(java.util.concurrent包):ConcurrentHashMap、ConcurrentLinkedDeque、CopyOnWriteArrayList、StringBuffer;

原创粉丝点击