java多线程以及线程池技术总结

线程
1.1 基本概念

程序 - 存放在硬盘/磁盘上的可执行文件。
进程 - 运行在内存中的程序。

操作系统中采用时间片轮转法来保证多个进程/线程并发执行,所谓的并发就是宏观并行,微观串行。

目前主流的操作系统都支持多进程,可以让操作系统同时执行多个任务,进程是重量级的,新建进程对系统的资源消耗比较大。
为了避免资源消耗过大,引出线程的概念。
线程是进程内部的程序流,共享所在进程的系统资源,通俗来说,操作系统支持多进程,而每一个进程支持多线程。
以后的主流的开发都采用多线程技术。

1.2 线程的创建

(1)Thread类的基本概念
java.lang.Thread类用于创建线程对象,java虚拟机允许同时运行多个线程。
创建和启动线程的主要方式:
a.自定义类继承自Thread类,并重写run()方法,调用start()方法启动线程。
b.自定义类实现Runnable接口,并重写run()方法,调用start()方法启动线程。

(2)Thread类的常用方法
Thread() - 使用无参的形式构造线程对象。
Thread(String name) - 使用参数指定的名称来构造线程对象。 Thread(Runnable target) - 根据参数指定的接口引用来构造线程对象。
Thread(Runnable target, String name) - 根据接口引用和线程名称共同构造对象。

void run() - 若使用Runnable接口作为参数创建了线程对象,则调用该接口中被重写以后的run()方法;否则该方法什么也不做就直接返回。 void start() - 使得线程启动起来,java虚拟机会自动调用该线程的run()方法。
(3)线程的原理分析
执行main()方法的线程叫做主线程,调用start()方法出现出来的线程叫做新/子线程
对于start()方法之前的代码来说,只会被主线程自己执行一遍,而start()方法一旦调用成功,该进程中的线程瞬间由1个变成了2个,其中主线程继续执行start()方法之后的语句块,而新启动的子线程去执行run()方法中的语句块。

当run()方法的方法体执行完毕时,子线程结束;当main()方法的方法体执行完毕时,主线程结束,主线程和子线程各自独立运行,没有执行的先后次序,取决于操作系统的调度算法。
经验:
目前创建线程的方式有两种:第一种继承的方式代码相对简单但不支持多继承;第二种实现接口的方式代码相对复杂但可以多实现并单继承,因此以后开发中推荐使用第二种方式。

1.3 线程的名称和编号

long getId() - 用于返回当前线程的标识符/编号。
String getName() - 用于返回当前线程的名称。
void setName(String name) - 用于修改当前线程的名称。
static Thread currentThread() - 用于返回当前正在执行的线程对象的引用。

1.4 线程的主要状态

新建状态—就绪状态—运行状态(或阻塞状态)—消亡状态


新建状态 - 当线程对象刚被创建出来的状态。
- 此时线程并没有开始执行。 就绪状态 - 当线程调用start()方法之后进入的状态。
- 此时线程还是没有开始执行。 运行状态 - 当线程调用调度器调度就绪状态的线程之后的状态
- 当时间片执行完毕但任务没有完成时回到就绪状态 消亡状态 - 当线程的任务已经完成时进入的状态。 阻塞状态 - 当线程的执行过程中发生了阻塞事件(sleep)进入的状态。
- 当阻塞状态解除之后进入就绪状态。

1.5 线程的基本操作

static void yield()
- 用于暂停当前正在执行的线程,转而执行其他线程。
- 让出CPU的执行权,回到就绪状态。
static void sleep(long millis)- 让当前正在执行的线程休眠参数指定的毫秒。
void interrupt() - 中断线程,通常用于打断睡眠。
void join() - 等待当前线程终止,没有时间的限制。
void join(long millis) -最多等待当前线程参数指定的毫秒。
void setPriority(int newPriority) - 更改线程的优先级。
int getPriority() - 获取线程的优先级。
- 优先级最高的线程不一定最先执行,只是获取时间片的机会更多一些而已。

重点:
sleep() 和 join()

2.线程的同步

StringBuilder类 - 后期增加的类,非线程安全的类,效率比较高。
- 不支持线程的同步技术。
StringBuffer类 - 早期就有的类,线程安全的类,效率比较低。
- 支持线程的同步技术。

2.1 基本概念

当多个线程在同一时刻访问同一种共享资源时,可能会造成数据的不一致等问题,为了避免该问题的发生,就需要对线程之间进行协调和通信,而线程之间的协调和通信就是线程同步机制。

2.2 解决方案

只需要将线程的并行改为串行就可以解决该问题。
此时可以解决问题,但是效率相对比较低,因此建议能不用则不用。

2.3 实现方法

为了实现线程的同步,需要借助java中synchronized关键字来保证原子性,具体实现方式如下:
(1)使用synchronized关键字修饰整个方法,语法格式:
访问修饰符 synchronized 返回值类型 方法名(形参列表){}
(2)使用synchronized关键字修饰方法中的部分代码,语法格式:
synchronized(this){
写入需要被锁定的语句块;
}
死锁:两个线程或多个线程相互锁定时
避免方式:顺序上锁,反向解锁

2.4 原理分析

当多个线程同时执行同一个方法时,为了避免线程之间的冲突问题,通常都会给该方法加上一把同步锁,当有线程先抢到同步锁时就可以进入同步语句块执行,其他线程只能进入阻塞状态,当该线程执行完毕同步语句块后会自动释放同步锁,阻塞的线程又可以抢占同步锁,抢占成功的线程去执行同步语句块,抢占不成功的线程继续阻塞。

线程和进程有什么区别?

一个进程是一个独立(self
contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。

线程池(集中管理多线程的)

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。

影响一个多线程应用程序的相应时间的几个主要因素之一是为每个任务生成一个线程的时间。

线程池的组成

1、线程池管理器(ThreadPoolManager):用于创建并管理线程池
2、工作线程(WorkThread): 线程池中线程
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
4、任务队列:用于存放没有处理的任务。提供一种缓冲机制。
线程池的优点
1)避免线程的创建和销毁带来的性能开销。
2)避免大量的线程间因互相抢占系统资源导致的阻塞现象。
3}能够对线程进行简单的管理并提供定时执行、间隔执行等功能。

代码部分

启动和创建线程的两种方式:
//继承自Thread类并重写run()方法,调用start()方法启动线程.
1.继承java.lang.Thread
2.public class SubThread extends Thread {
    @Override
    public void run() {
    sys..................
    }
    public static void main(String[] args) {
    Thread t1 = new 类名();
    t1.stack();
    sys..................
    }
}
//自定义实现Runnable接口,并重写run()方法,调用start()方法启动线程.
1.实现Runnable接口
2.public class SubRunnable(类名) implements Runnable{
    @Override
    public void run(){
        system.......................;
    }
    public static void main(String[] args){
        Thread t1 = new Thread(new SubRunnable());
        t1.start();
        system.........................;

    }
}


----------


//匿名内部类
public class TestNoNameStrat extends Thread {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run(){
                System.out.println("左手星辰雨下");
            }
        }.start();
        new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("莫颜世界");
            }
        }).start();
    }
}


----------


//子线程、主线程的编号
public class TestNames extends Thread {
    public TestNames(String name){
        super(name);

    }
    public void run(){
        System.out.println("子线程"+getId()+"姓名"+getName());
        this.setName("诸葛亮");
        System.out.println("子线程"+getId()+"姓名"+getName());
    }
    public static void main(String[] args) {
        Thread t1 = new TestNames("刘备");
        t1.start();
        Thread t =Thread.currentThread();
        System.out.println("主线程"+t.getId()+"姓名"+t.getName());
    }

}


----------


public class TestNoNameStrat extends Thread {
public static void main(String[] args) {

        //1.将继承的方式改为匿名内部类的策略实现
        new Thread(){
            @Override
            public void run(){
                System.out.println("世界上最痴情的等待,就是你"
                        + "在switch我在case,也许永远都不会选择我!");
            }
        }.start();

//2.将接口的方式改为匿名内部类的策略实现
new Thread(new Runnable(){
    @Override
    public void run() {
        System.out.println("世界上最真情的相依,就是你在try我在catch"+ ",无论你发神马脾气,我都静静地处理!");
            }
        }).start();
    }
}


----------


//模拟ATM去后台服务器读取账户余额的过程
public class Account implements Runnable {

    private int money = 1000; //账户余额

    @Override
    public /*synchronized*/ void run() {
        synchronized(this){
            //模拟ATM去后台服务器读取账户余额的过程
            int temp = money;
            //判断账户余额是否>=200元
            if(temp >= 200){
                //模拟取款的过程
                System.out.println("正在出钞,请稍后...");
                temp -= 200; // temp = temp - 200; 800 800
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("请取走您的钞票!");
            }
            else{
                System.out.println("账户余额不足,请重新核对金额!");
            }
            //将ATM中处理之后的账户余额重写写入后台的服务器
            money = temp;  //800 800
        }
    }

    public static void main(String[] args) {
        Account acc = new Account();
        Thread t1 = new Thread(acc);
        Thread t2 = new Thread(acc);
        t1.start();
        t2.start();
        //让主线程等待子线程终止,然后打印账户余额
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("最后的账户余额是:" + acc.money);//800
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值