Java多线程入门

线程

概念介绍

CPU的核数:瞬时时间能处理的任务数

CPU的主频:频繁的切换以至于可以“同时”运行更多的程序

程序(program):为完成特定任务,用某种语言编写的一组指令的集合。即静态代码、静态对象。

进程(progress):正在运行的一个程序

线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。

多线程,一个进程(一个程序运行时),可以分化为并行执行多个线程(多个子程序)

进程相当于一条河,线程相当于河的分支。

何时需要多线程

程序需要同时执行两个或多个任务。

程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。

需要一些后台运行的程序时。

多线程的实现

通过继承Thread实现

TestThread.java

/**
 * 继承Thread的方式实现多线程
 *
 * @Author Nino 2019/10/3
 */
public class TestThread extends Thread {
    @Override
    public void run() {
        System.out.println("多线程运行的代码");
        for (int i = 0; i < 5; i++) {
            System.out.println("这是多线程的逻辑代码:" + i);
        }
    }
}

Main

public static void main(String[] args) {
    Thread thread = new TestThread();
    thread.start();// 启动线程
    System.out.println("---------------");
    System.out.println("---------------");
    System.out.println("---------------");
}

多线程的异步

当main执行start方法开启多线程之后,相当于在main方法之外开启了一个支流,在这往后的main函数中的其他代码的运行就与run方法的运行无关了。

通过实现Runnable的接口实现

TestRunnable.java

/**
 * 通过实现Runnable接口方式实现多线程
 *
 * @Author Nino 2019/10/3
 */
public class TestRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"Runnable多线程运行的代码");
        for (int i = 0; i < 5; i++) {
            System.out.println("这是Runnable多线程的逻辑代码:" + i);
        }
    }
}

main

public static void main(String[] args) {
    Thread thread1 = new Thread(new TestRunnable(), "t-1");
    thread1.start();

    System.out.println("---------------");
    System.out.println("---------------");
    System.out.println("---------------");
}

以上两种实现方法的对比

区别

继承Thread:线程代码存放在Thread子类run方法中

实现Runnable:线程代码放在实现接口的子类中。

共性

都需要Thread父类来完成run和start操作

实际使用中更常用的是实现方法,好处如下:

  1. 避免了单继承的局限性
  2. 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

第二点可以看以下代码

TestRunnable.java
/**
 * 通过实现Runnable接口方式实现多线程
 *
 * @Author Nino 2019/10/3
 */
public class TestRunnable implements Runnable {

    int count = 0;
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"Runnable多线程运行的代码");
        for (int i = 0; i < 5; i++) {
            count++;
            System.out.println(Thread.currentThread().getName() + "这是Runnable多线程的逻辑代码:" + count);
        }
    }
}


Main.java
/**
 * @Author Nino 2019/10/3
 */
public class Main {

    public static void main(String[] args) {
        Runnable run = new TestRunnable();
        Thread thread = new Thread(run, "t-1");
        Thread thread1 = new Thread(run, "t-2");
        thread.start();
        thread1.start();
    }
}

输出:

t-2Runnable多线程运行的代码
t-1Runnable多线程运行的代码
t-2这是Runnable多线程的逻辑代码:1
t-1这是Runnable多线程的逻辑代码:2
t-2这是Runnable多线程的逻辑代码:3
t-1这是Runnable多线程的逻辑代码:4
t-2这是Runnable多线程的逻辑代码:5
t-1这是Runnable多线程的逻辑代码:6
t-2这是Runnable多线程的逻辑代码:7
t-1这是Runnable多线程的逻辑代码:8
t-2这是Runnable多线程的逻辑代码:9
t-1这是Runnable多线程的逻辑代码:10

可以明显看出两个线程在共享对象,处理同一资源。

使用多线程的优点

  1. 提高应用程序的响应,增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构,将长进程分为多个线程,独立运行。

Thread类有关的方法

void start();
run();
String getName();
void setName();
static currentThread();// 当前线程

线程的优先级

即哪个线程有较大的概率被执行

优先级只可以增大概率 ,并不一定是优先执行

优先级用数字分为[1, 10],数字越大,优先级越高,如果没有设置,默认优先级为5

方法:

int getPriority();
void setPriority(int);
//线程让步
//暂停当前执行中的线程,把机会让给优先级相同或更高的线程
//如没有这样的线程,则忽视
static void yield();
//阻塞其余线程,直到join进来的线程执行完为止
void join();
//睡眠
void sleep(long ?ms);
//强制结束线程生命周期
void stop();
//判断当前线程是否还活着
boolean isAlive();

线程的生命周期

  1. 新建状态,当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值

  2. 就绪状态,当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行

  3. 运行状态,如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态

  4. 阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态

  5. 死亡状态,线程完成了全部工作或被提前强制中止

在这里插入图片描述

线程的同步

为什么要线程同步

  • 多个线程执行的不确定性会引起执行结果的不稳定
  • 多个线程对账本的共享,会造成操作的不完整性,会破坏数据
    在这里插入图片描述

通过synchronized同步锁完成同步

public synchronized void xxx()

只需要直接在可能发生问题的方法上加上synchronized关键字就可以。

注意:在普通方法上加同步锁synchronized,锁的是整个对象,并不单单是这个方法,如果其他方法也加上同步锁,则共享同步锁。

不同的对象是不同的锁

静态的方法synchronized将会共用一个锁。

对代码块加入同步锁
// 表示传进来的对象要被同步锁,一般是this
synchronized(对象){ 

}

用this锁代码块是代表当前的对象被锁,如果在其他方法中也有synchronized(this)的代码块,使用的都是同一个异步锁。

要想不使用同一个异步锁,给synchronized(对象)传递一个非this的不同对象即可。

线程的死锁问题

原因
  • 当前线程拥有其他线程需要的资源
  • 当前线程等待其他线程已拥有的资源
  • 都不放弃自己拥有的资源

比如:
有线程a0,需要执行方法f0
有线程a1,需要执行方法f1
f0和f1都有同步锁的方法
现在发生了:
a0调用f1方法并且一直没有执行完f1
a1调用f0方法并且一直没有执行完f0
双方线程都在等对方释放方法,对方都不释放,形成死锁

解决方法
  • 专门的算法,比如加锁的顺序一致
  • 尽量减少同步资源的定义

线程通信

主要有以下几种方法,必须在synchronized修饰的方法中或者代码块下才可用,不然会报异常。

void wait(); // 令当前线程暂时挂起并放弃CPU、同步资源,进入排队等待
void notify(); // 唤醒正在排队的优先级最高的线程,使其结束等待
void notifyAll(); // 唤醒正在排队等待资源的所有线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值