多线程笔记
多线程
进程
- 操作系统将运行在系统中的程序叫做进程,每个程序操作系统都会分配独立的CPU的指令和内存空间,每个程序在系统中都有唯一的进程号
- 特点资源开销较大,进程间难以通信。
线程
-
线程相对于进程而言,线程在进程中执行,依托于进程,多个线程共享进程的内存空间等资源。线程主要为提高程序的运行效率。
-
特点:资源占用小,线程间通信开销小,相对容易。
线程的运行模式两种
- 分时调度:进程比较常用,所有的线程轮流使用CPU资源,排队使用。
- 抢占式调度:根据线程调度的优先级来确定,谁优先占有CPU资源。
Java中如何实现线程的编程
-
同时执行多个任务,提升程序的执行效率。
Java中的多线程实现:
-
通过集成Thread(线程)类,重写run方法,将任务代码块放到run中来执行。
-
通过多线程的类实现Runnable接口,实现run()方法,将任务代码块放到run方法中来执行。
-
通过多线程的类来实现Callable接口,实现call()方法,将任务代码块放到call()方法中来执行
方法一
通过Thread类来实现多线程
//构造方法 Thread() //分配一个新的 Thread对象。 Thread(Runnable target) //分配一个新的 Thread对象。 Thread(Runnable target, String name) //分配一个新的 Thread对象。 Thread(String name) //分配一个新的 Thread对象。 Thread(ThreadGroup group, Runnable target) //分配一个新的 Thread对象。 Thread(ThreadGroup group, Runnable target, String name) //分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,属于 group引用的线程组。 Thread(ThreadGroup group, Runnable target, String name, long stackSize) //分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,并且属于 group引用的线程组,并具有指定的 堆栈大小 。 Thread(ThreadGroup group, Runnable target, String name, long stackSize, boolean inheritThreadLocals) //分配一个新的Thread对象,使其具有target作为其运行对象,具有指定的name作为其名称,属于group引用的线程组,具有指定的stackSize ,如果inheritThreadLocals为true ,则继承inheritable thread-local变量的初始值。 Thread(ThreadGroup group, String name) //分配一个新的 Thread对象。 //常用方法 join()://让步 wait()://等待 sleep()://休眠 interrupt()://中断 currentThread()://返回当前正在执行的线程对象
开发步骤:
- 开发一个Java类,继承Thread类
- 重写Thread类中的run方法
- 将需要通过多线程执行的代码放入run方法体重。
- 通过Thread类提供start方法启动线程
实现代码:
public class ThreadDemo03 extends Thread { public ThreadDemo03(String name){ super(name); } @Override public void run() { //此处写需要通过多线程执行的代码 for (int i = 0; i <100 ; i++) { Thread current = Thread.currentThread(); System.out.println(current.getName()+"线程正在执行:"+i); } } public static void main(String[] args) { //创建线程类的实例 ThreadDemo03 th01 = new ThreadDemo03("张三"); ThreadDemo03 th02 = new ThreadDemo03("李四"); th01.start(); th02.start(); for (int i = 0; i <500 ; i++) { Thread mainThread = Thread.currentThread();//创建main的线程对象 System.out.println(mainThread.getName()+"线程正在执行:"+i); } } } /* *Java中main方法也是一个主线程,并且优先级比较高的一个主线程运行时出现三个线程分别输出的结果,默认线程名称 为main *如果直接调用run方法则不能启动多线程程序按照顺序执行 *可以调用Thread父类的构造方法super(name)来给线程名称,如果不给默认为Thread-"num" */
第二种实现方法(通过实现runnable接口)
开发步骤:
- 通过线程的实现类,实现有Runnable。
- 实现接口中的run()方法
- 将线程执行的代码块放入的run()方法体中。
- 创建该实现类的对象。
- 床架Thread类的对象,将线程的实现类,通过构造方法传入。
- 通过Thread类的start方法,启动线程,并调用实现类中的run方法。
//Runnable接口的实现 public class ThreadImpDemo01 implements Runnable{ @Override public void run() { for (int i = 0; i <100 ; i++) { Thread current = Thread.currentThread(); System.out.println(current.getName()+"线程正在执行:"+i); } } } //测试 public static void main(String[] args) { Runnable runnable = new ThreadImpDemo01(); /*因为线程的启动是通过start方法啊,而实现Runnable接口的线程类没有该接口, *必须通过Thread线程类提供的start方法来启动线程。 * Thread(Runnable target):提供启动Runnable接口实现类的线程入口。 */ Thread th01 = new Thread(runnable); Thread th02 = new Thread(runnable); Thread th03 = new Thread(runnable2; th01.start();//此时启动的是Runnabl3对象中的方法。 th02.start(); th03.start(); }
-
问题解答
如何实现Runnable接口多个线程实例打开?
只需要将Thread创建多个对象并启动就行了
Thread和Runnable两者的区别是什么?
-
实现Runnable接口的方式:
-
实现Runnable接口,还可以实现其他的业务接口,体现多态。
-
实现Runnable接口线程是共享一个实例的数据。
-
线程池中目前仅支持Runnable接口的实现和Callable接口的实现。
-
能够更好的扩展当前类,更好体现编程的优势。
-
-
继承Thread类实现线程的方式:
-
当前的类之策单一,继承Thread类之后,不允许在继承其他的类。
-
更好的使用Thread的方法
线程安全问题:当某个线程进入休眠时,还未修改变量值,而其他线程也进入了,引发线程安全问题。
解决方法为实现线程同步。
Java中通过synchronized同步机制:将当前线程控制的区域设为同步区域,则其他的线程等待当前线程操作完 成之后进入。
如何将当前线程控制的区域设为同步区域呢?
-
设置同步代码块区域(对象同步)
synchronized(对象名){
//同步代码块
}
可以使用this关键字来指定对象,Object o = Object()
-
设为同步方法区域
将可能一发线程安全问题的代码,通过方法来定义,并将改方法低昂已成同步方法,若当前有一个线程进入该方法执行,其他线程必须在该方法外边等待,当前线程调用执行完成后,其他的线程才允许调用该方法。使用synchronized关键词修饰方法。
普通线程调用和使用的缺点
启动几个线程就需要实例化Thread类几次
- 每次启动New对象,系统开销大,性能差。
2. 线程缺乏管理,相互竞争及可能占用过多系统资源,导致OOM(内存溢出)。
3. 缺乏更多对线程的操作管理。
方法三 线程池(Callable)
- 线程池的优点
- 重用线程池中的线程,减少线程的创建与消亡,性能较好。
- 有效的控制线程的并发数,提高系统性能。
- 避免过多的资源竞争和线程阻塞。
- 什么是线程池?
- 能容纳多个线程的一种容易,可以存放多个线程,可以反复使用这些线程。不用反复多次的创建线 程。
- 可以对线程池中的线程进行统一的管理,包括调度管理,周期管理等。
- 主要对象:
- Executor类:线程池的根类型,负责线程调度的根接口。
- ExecutorService:子接口,主要是管理系统池的常用方法。
- ThreadPoolExecutor:线程的主要实现类。
- ScheduleExecutors类:线程的的调度类
- 线程池的使用步骤:
- 创建线程池对象。
- 创建线程任务实例。
- 提交线程任务(启动线程任务)
- 关闭线程池
- 代码实现
public static void main(String[] args) {
//创建线程池对象
ExecutorService executorService = Executors.newFixedThreadPool(3);
//创建线程池对象
Runnable thread = new RunnableImp("张三");
//提交线程任务
executorService.submit(thread);
//关闭线程池
executorService.shutdown();
}
- 多线程的调用
public static void main(String[] args) {
final ArrayList<Future<Integer>> allcalvalue = new ArrayList();
//创建线程池对象
ExecutorService executorService = Executors.newFixedThreadPool(3);
//创建线程池对象
Callable callableImp = new CallableImp();
//提交线程任务
try {
for (int i = 0; i < 3; i++) {
Future<Integer> callvalue = executorService.submit(callableImp);
allcalvalue.add(callvalue);
}
for (Future<Integer> future:allcalvalue) {
System.out.println("收到的随机数为"+future.get());
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
//关闭线程池
executorService.shutdown();