Java多线程学习总结

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.java
package 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.java
package 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;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值