0%

java多线程基础部分

文章字数:1413,阅读全文大约需要5分钟

基本概念

  • 进程:进程拥有独立的代码和数据空间,是资源分配的最小单位。一个进程可以包含一个或多个线程。
  • 线程:同一类的线程共享代码和数据空间,每个线程都拥有独立的运行栈和程序计数器。线程是cpu调度的最小单位。
  • 进程/线程 的五个状态:创建就绪运行阻塞终止
  • 多进程代表操作系统可运行多程序,线程代表程序里运行多个顺序流

创建java线程

java创建线程有三种方法

  • 基础Thread类
  • 实现Runable接口
  • 实现Callable接口,配合Future、线程池

继承Thread

1
2
3
4
5
6
7
8
9
10
11
class threadLearn extends Thread{
public void run(){
//...
}
}
public class Main{
public static void main(){
ThreadLearn learn = new ThreadLearn ();
learn.start();
}
}

实现java.lang.Runnable接口

1
2
3
4
5
6
7
8
9
10
11
12
class ThreadLearn implements Runnable{
@Override
public void run(){
//...
}
}
public class Main{
public static void main(){
ThreadLearn learn = new ThreadLearn ();
learn.start();
}
}
  1. start()方法使线程变成可运行状态Runnable,操作系统决定何时调用运行。
  2. start()方法重复调用会抛出java.lang.IllegalThreadStateException异常
  3. run()方法是多线程程序的一个约定,所有的多线程代码都要写在里面。

ThreadRunnable比较

  1. Thread本质上是实现了Runnable接口。
  2. Thread类实现了Runnable并在其之上进行了拓展。

线程状态

线程状态转换

线程状态 状态分类 描述
新建状态New - 新创建了一个线程对象
就绪状态Runnable - 其它线程调用了该线程的start()方法,线程变为可运行
运行状态Running - 就绪状态的线程被系统调用。
阻塞状态Blocked - 分为三种情况
阻塞状态 等待阻塞 运行的线程执行wait()方法
阻塞状态 同步阻塞 获取同步锁时锁被其它线程占用,jvm会把该线程放入锁池之中
阻塞状态 其它阻塞 线程运行sleep()join()的线程结束或发出I/O请求。sleep不会释放锁
死亡状态 - 线程执行完毕或异常退出

线程调度

  1. 线程优先级
  • Thread的setPriortity()getPriortity()管理优先级
  • 优先级取值1~10整数,推荐使用常量,这三个级别的可移植性好。
    1
    2
    3
    static int MAX_PRIORITY=10;
    static int NORM_PRIORITY=5;//默认
    static int MIN_PRIORITY=1;
  1. 线程睡眠
    Thread.sleep(long millis):设定线程阻塞时间,阻塞结束后进入就绪状态。

  2. 线程等待
    Object类中的wait()方法,导致当前线程等待,直到其它线程调用此对象的notify()方法或者notifyAll()唤醒,等价于wait(0)

  3. 线程让步
    Thread.yield()暂停当前正在执行的线程对象,把执行的机会让给优先级相同或更高的线程。

  4. 线程加入
    join()当前线程进入阻塞态,调用其它线程,该线程运行完毕后当前线程再进入就绪态。

  5. 线程唤醒
    Object类的notify()方法,唤醒对象监听器上一个线程,如果有多个线程在此对象上等待,则唤醒随机一个。notifyAll()唤醒所有该对象上等待的线程。
    wait()sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException

sleep方法不会释放锁,wait
waitnotifynotifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用

常用方法

  • sleep(): 强迫一个线程睡眠N毫秒。
  • isAlive(): 判断一个线程是否存活。
  • join(): 等待线程终止。
  • activeCount(): 程序中活跃的线程数。
  • enumerate(): 枚举程序中的线程。
  • currentThread(): 得到当前线程。
  • isDaemon(): 一个线程是否为守护线程。
  • setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于没有用户线程后守护线程终止)
  • setName(): 为线程设置一个名称。
  • wait(): 强迫一个线程等待。
  • notify(): 通知一个线程继续运行。
  • setPriority(): 设置一个线程的优先级。

线程同步

synchronized[ˈsɪŋkrənaɪzd]是系统级的锁,一旦锁死除了线程自行释放没有其它方法。juc的lock锁是编码级别的,可以代码解锁。juc(java.util.concurrent)下次讨论

  • synchronized在对象里:标记于方法或是代码块都是对对象加锁。只要对象中出发了锁,整个对象都无法进入。
  • synchronized标记于静态方法,则是对于类加锁,和对象锁不冲突

线程数据传递

同步情况下使用参数传入,return返回的形式,多线程下运行和结束是不可预料的,所以无法和同步一样传参。

  1. 使用构造方法传参

    1
    2
    //线程使用构造函数就收这个参数
    Thread thread = new MyThread1("hello world");
  2. set方法
    线程里先设置set方法接受参数(不用多说了吧)
    然后start()之前设置参数

    1
    2
    3
    4
    MyThread2 myThread = new MyThread2();   
    myThread.setName("hello world");
    Thread thread = new Thread(myThread);
    thread.start();
  3. 回调函数

    将对象传入线程,线程在某一时间调用对象的函数。主线程通过传入的对象获取线程操作后的值。(还有静态类)

  4. 声明lambda函数的接口

    1
    2
    3
    public interface ICallback {   
    public void callback(Map<String, Object> params);
    }
  5. 线程调用接口返回数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public static void doStm(final ICallback callback) {  
    // 初始化一个线程
    Thread t = new Thread() {
    public void run() {

    // 这里是业务逻辑处理
    System.out.println("子线任务执行:"+Thread.currentThread().getId());

    // 为了能看出效果 ,让当前线程阻塞5秒
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    // 处理完业务逻辑,
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("a1", "这是我返回的参数字符串...");
    callback.callback(params);
    };
    };

    es.execute(t);
    //一定要调用这个方法,不然executorService.isTerminated()永远不为true
    es.shutdown();
    }
    1
    2
    3
    doStm((params)->{
    System.out.println("单个线程也已经处理完毕了,返回参数a1=" + params.get("a1"));
    });