JAVA多线程
1,java多线程入门
1.1,进程
是正在运行的程序
-
是系统进行资源分配和调用的独立单位
-
每一个进程都有它自己的内存空间和系统资源
1.2,线程
是进程中的单个顺序控制流,是一条执行路径。
-
单线程:一个进程如果只有一条执行路径,则称为单线程程序
-
多线程:一个进程若果有多条执行路径,则称为多线程程序。
1.3,多线程的实现方式
1.3.1,继承Thread
// 1,继承Thread
public class TextThread extends Thread {
// 2,重写run方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是run");
}
}
public static void main(String[] args) {
// 3,创建线程实例对象
TextThread textThread = new TextThread();
// 4,调用start方法
textThread.start();
for (int i = 0; i < 20; i++) {
System.out.println("我是main");
}
}
}
- 继承Thread
- 重写run方法
- 创建线程实例对象
- 调用start方法
run():封装线程执行的代码,直接调用相当于普通方法的调用
start():启动线程,然后由JVM调用此线程的run()方法
1.3.2,实现Runnable接口
// 1,实现Runnable接口
public class TextThread implements Runnable {
// 2,重写run方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是run");
}
}
public static void main(String[] args) {
// 3,创建Runnable接口的实现类对象
TextThread textThread = new TextThread();
// 4, 创建线程实例对象
// 可以给线程起名字
Thread thread = new Thread(textThread);
// 5,调用start方法
// new Thread(textThread).start()
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println("我是main");
}
}
}
- 实现Runnable接口
- 重写run方法
- 创建Runnable接口的实现类对象
- 创建线程实例对象
- 调用start方法
1.3.3,实现Callable接口
import java.util.concurrent.*;
// 实现Callable接口
public class TextCallable implements Callable<Boolean> {
// 重写call方法
@Override
public Boolean call() {
return true;
}
public static void main(String[] args) {
// 创建对象
TextCallable t1 = new TextCallable();
// 创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> submit1 = executorService.submit(t1);
Future<Boolean> submit2 = executorService.submit(t1);
Future<Boolean> submit3 = executorService.submit(t1);
// 获取结果
try {
boolean r1 = submit1.get();
boolean r2 = submit2.get();
boolean r3 = submit3.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 关闭服务
executorService.shutdownNow();
}
}
- 实现Callable接口
- 重写call方法
- 创建对象
- 创建执行服务
- 提交执行
- 获取结果
- 关闭服务
1.3.4,知识卡片
静态代理:
- 目标对象和代理对象都要实现同一个接口
- 代理对象中有目标对象的入口
优点
- 目标对象专注于做自己的事情,其他都交给代理对象
案例:模拟龟兔赛跑
public class Race implements Runnable {
private static String winner;
@Override
public void run() {
for(int i = 0;i <= 100;i++) {
boolean flag = gameOver(i);
if (flag) {
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
}
}
public boolean gameOver(int steps) {
if(winner != null) {
return true;
}
if (steps == 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
1.4,设置和获取线程的名称
获取线程名称:getName()方法
设置线程名称:setName()方法
currentThread():返回当前正在执行的线程对象的引用
1.5,线程生命周期
- 不要用JDK的方法停止线程
- 最好是线程自己停下来
- 建议使用一个标志位停止线程
sleep()方法:是当前正在执行的线程暂缓执行
- 可以模拟网络延时,放大问题的发生性,检查代码
- 模拟倒计时
- 打印当前系统时间
yield()方法:让CPU重新调度线程
join()方法:让线程强制执行,执行完后,在执行其他线程
1.6,线程优先级
getPriority()方法:返回此线程的优先级
setPriority()方法:设置此线程优先级,范围是1-10
1.7,用户线程和守护线程
虚拟机必须保证用户线程执行完毕
虚拟机不必保证守护线程执行完毕
setDaemon():将线程设置为守护线程
1.8,线程同步
并发:同一个对象被多个线程同时操作,会有并发问题
同步方法:
public synchronized void method() {
}
同步方法控制对对象的访问,每一个对象对应着一把锁,每个同步方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行,解决并发问题
缺陷:效率下降
同步块:
synchronized(Obj) {
}
Obj:称之为 同步监视器
- Obj 可以是任意对象,但是推荐使用共享资源作为Obj
- 同步方法的锁是this
同步监视器的执行过程
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,释放锁
- 第二个线程访问,发现同步监视器没锁,然后锁定并访问
1.9,死锁
两个对象都希望获得对方的锁而互相僵持导致死锁
1.10,Lock锁
jdk5开始,提供了更加强大的线程同步机制,通过显示定义同步锁对对象实现同步,同步锁使用Lock对象充当
ReentrantLock类实现了Lock,比较常用
1.11,线程池
ExecutorService和Executors
可以参考JDK帮助文档