1.概念
- 线程(thread)是一个程序内部的一条执行路径
- main方法的执行其实就是一个单独的执行路径
- 程序中如果只有一条执行路径,那么这个程序就是单线程的程序
2.多线程是什么
多线程是指程序中有多条执行流程的技术
例如:消息通信,淘宝,京东系统都离不开多线程技术
1.继承Thread类
- 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法.
- 创建MyThread类的对象.
- 调用线程对象的start()方法启动线程(启动后还是执行run()方法的).
- 注意:直接调用run()方法会被当做普通方法执行,此时相当于单线程执行
- 只有调用start方法才是启动一个新的线程执行
- 把主线程任务放在子线程之前会出现什么问题?这样主线程一直会先跑完的,相当于是一个单线程的效果了
public static void main(String[] args) {
//main方法本身是一个单线程在执行的,称为主线程
//3.创建线程对象
Thread t=new Mythread();
//4.调用线程对象的start方法启动线程(最终还是调用线程的run方法)
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程输出"+i);
}
}
}
/**1.定义线程类继承了Thread类
* */
class Mythread extends Thread{
//2.重写了run方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程输出"+i);
}
}
}
2.实现Runnable接口
- ①定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
- ②创建MyRunnable任务对象
- ③把MyRunnable任务对象交给Thread处理
- ④调用线程对象的start()方法启动线程
public static void main(String[] args) {
//3.创建一个线程任务类的对象(此对象不是线程对象)
Runnable target=new MyRunnable();
//4.把线程任务对象交给Thread线程对象
Thread t=new Thread(target);
//5.启动多线程
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("main线程任务输出"+i);
}
}
}
/**1.定义一个线程任务类实现Runnable接口
* */
class MyRunnable implements Runnable{
//2.重写run()方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程任务输出"+i);
}
}
}
3.实现Runnable接口(使用匿名内部类)
还可以用Lamdba表达式简化
Runnable target1=new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程任务2输出" + i);
}
}
};
Thread t1=new Thread(target1);
t1.start();
4. 实现Callable,FutureTask接口实现
- 得到任务对象:①定义类实现Callable接口,实现call方法,封装要做的事情②用FutureTask和Callable对象封装成线程任务对象
- 把线程任务对象交给Thread处理
- 调用Thread的start方法启动线程,执行任务
- 线程执行完毕后,通过FutureTask的get方法去获取任务执行的结果
public static void main(String[] args) {
//3.创建一个Callable的执行对象
Callable<String> call1=new MyCallable(100);
//4.把Callable包装成未来任务对象
FutureTask<String> f1=new FutureTask<>(call1);
//5.交给Thread对象
Thread t1=new Thread(f1);
//6.启动线程
t1.start();
Callable<String> call2=new MyCallable(999);
FutureTask<String> f2=new FutureTask<>(call2);
Thread t2=new Thread(f2);
t2.start();
//7.得到线程执行完毕后的结果
try {
//主线程执行到这里,如果上面第一个线程没有执行完毕,这里会让出cpu,等待第一个线程执行完毕之后才能执行这个代码取结果
String s1=f1.get();
System.out.println(s1);
} catch (Exception e) {
e.printStackTrace();
}
try {
String s2=f2.get();
System.out.println(s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 1.定义一个类实现callable接口
*/
class MyCallable implements Callable<String>{
private int n;
public MyCallable(int n){
this.n=n;
}
//2.重写call方法,定义执行的任务和返回的结果
@Override
public String call() throws Exception {
//需求:计算0-100的和
int sum=0;
for (int i = 0; i < n; i++) {
sum+=i;
}
return "子线程帮助计算0-"+n+"的和是:"+sum;
}
}
3.Thread常用方法
4.线程安全问题
多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题
1)线程安全问题出现的原因?
- 存在多线程并发
- 同时访问共享资源
- 存在修改共享资源
2)线程安全问题的解决方案
- 解决方案1:同步代码块
synchronized (this) {
出现问题的代码
}
- 解决方案2:同步方法:出现问题的方法用synchronized修饰
public synchronized void drawMoney(double money){
}
- 解决方案3:lock锁
//锁对象属于账户对象,而且唯一不可更改
private final ReentrantLock lock=new ReentrantLock();
lock.lock();//上锁
try{
}
finally {
lock.unlock();//解锁
}
5.线程通信
1)概念:线程间相互发送数据.
2)常见形式
- 通过共享一个数据的方式实现
- 根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做
3)线程通信的三个方法
- wait()
- notify()
- notifyAll()
注意:上述方法应该使用当前同步锁对象进行调用
6.线程池(重点)
概念:可以复用的技术;
代表:JDK5.0起提供了代表线程的接口:ExecutorService
如何得到线程池对象?
- 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
- 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
1)面试高频题目:
临时线程什么时候创建?新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程.
什么时候会开始拒绝任务?核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝.
2)Executor方法![](https://i-blog.csdnimg.cn/blog_migrate/70636d78122e0013e3bafaef92b32a93.png)
3)新任务的拒绝策略
4)线程池操作Runnable任务
public static void main(String[] args) {
/** public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
* */
Executor pool=new ThreadPoolExecutor(3,5,10, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//提交任务给线程处理
Runnable my=new MyRun();
pool.execute(my);
pool.execute(my);
pool.execute(my);
pool.execute(my);
pool.execute(my);
pool.execute(my);//排满了,临时线程的时机
pool.execute(my);
pool.execute(my); //出现任务拒绝的时机
pool.execute(my);
}
}
/**1.定义一个线程任务类实现Runnable接口
* */
class MyRun implements Runnable{
//2.重写run()方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"输出"+i);
}
try {
Thread.sleep(100000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
5)线程池操作callable任务
public class d6_线程池实现callable {
public static void main(String[] args) {
ExecutorService pool1=new ThreadPoolExecutor(3,5
,10, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
Future<String> s1 = pool1.submit(new MyCall(100));
try {
System.out.println(s1.get());
} catch (Exception e) {
e.printStackTrace();
}
Future<String> s2 = pool1.submit(new MyCall(200));
try {
System.out.println(s2.get());
} catch (Exception e) {
e.printStackTrace();
}
Future<String> s3 = pool1.submit(new MyCall(300));
try {
System.out.println(s3.get());
} catch (Exception e) {
e.printStackTrace();
}
Future<String> s4 = pool1.submit(new MyCall(400));
try {
System.out.println(s4.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 1.定义一个类实现callable接口
*/
class MyCall implements Callable<String> {
private int n;
public MyCall(int n){
this.n=n;
}
//2.重写call方法,定义执行的任务和返回的结果
@Override
public String call() throws Exception {
//需求:计算0-100的和
int sum=0;
for (int i = 0; i < n; i++) {
sum+=i;
}
return Thread.currentThread().getName()+"计算1-"+n+"的和是:"+sum;
}
}
6)Executors使用可能存在的陷阱
public class d7_线程池 {
public static void main(String[] args) {
ExecutorService pool=Executors.newFixedThreadPool(3);
pool.submit(new MyRun1());
pool.submit(new MyRun1());
pool.submit(new MyRun1());
pool.submit(new MyRun1());
pool.submit(new MyRun1());
pool.submit(new MyRun1());
}
}
/**1.定义一个线程任务类实现Runnable接口
* */
class MyRun1 implements Runnable{
//2.重写run()方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"输出"+i);
}
try {
Thread.sleep(100000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.定时器
- 推荐使用ScheduleExecutorService
- 基于线程池 某个任务执行出现问题的情况不会影响其他定时任务的执行
ScheduledThreadPoolExecutor pool=new ScheduledThreadPoolExecutor(3);
pool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----aaaa");
}
},0,2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"----bbbbb");
}
},0,1, TimeUnit.SECONDS);
8.并发和并行
概念:正在运行的程序(软件)被称为进程,线程是输入进程的,多线程其实是并发与并行同时存在的
并发理解:
- CPU同时执行线程的数量:与CPU的核数和逻辑处理器有关系;
- CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们感觉这些线程在同时执行,这就是并发
并行的理解:同一时刻同时在执行