一、什么是线程?
- 是一个程序内部的一条执行流程
多线程是什么?
- 多条线程由CPU负责调度执行
多线程的创建方式一:继承Thread类
//1.继承Thread类
public class MyThread extends Thread {//2.必须重写run方法@Overridepublic void run() {for (int i = 1; i <= 5 ; i++) {System.out.println("子线程MyThread输出 :" + i);}}
}public class ThreadTest1 {//main方法是由一条默认的主线程负责执行public static void main(String[] args) {//3.创建MyThread线程类的对象代表一个线程Thread t = new MyThread();//4.启动线程(自动执行run方法)t.start();for (int i = 1; i <= 5 ; i++) {System.out.println("主线程main输出:" + i);}}
}
多线程的创建方式二:实现Runnable接口
//1.实现Runnable接口
public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 1; i <= 5 ; i++) {System.out.println("子线程输出:" + i);}}
}public class ThreadTest1 {public static void main(String[] args) {Runnable target = new MyRunnable();new Thread(target).start();for (int i = 1; i <= 5 ; i++) {System.out.println("主线程main输出:" + i);}}
}
匿名内部类的写法
public class ThreadTest1 {public static void main(String[] args) {//直接创建Runnable接口的匿名内部类Runnable target = new MyRunnable(){@Overridepublic void run() {for (int i = 1; i <= 5 ; i++) {System.out.println("子线程1输出:" + i);}}};new Thread(target).start();//简化形式1:new Thread(new MyRunnable(){@Overridepublic void run() {for (int i = 1; i <= 5 ; i++) {System.out.println("子线程2输出:" + i);}}}).start();//简化形式2new Thread(() -> {for (int i = 1; i <= 5 ; i++) {System.out.println("子线程3
多线程的创建方式三:;利用Callable接口、FutureTask类来实现
//1.让这个类实现Callable接口
public class MyCallable implements Callable<String> {private int n;public MyCallable(int n) {this.n = n;}//2.重写call方法@Overridepublic String call() throws Exception {int sum = 0;for (int i = 1; i <= n ; i++) {sum += i;}return "线程求出了1-" + n + "的和是:" + sum;}
}public static void main(String[] args) throws ExecutionException, InterruptedException {//3.创建一个Callable对象Callable call = new MyCallable(100);//4.把Callable的对象封装成一个FutureTask对象//未来对象的作用?//1.是一个任务对象,实现了Runnable对象//2.可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后FutureTask<String> f1 = new FutureTask<>(call);new Thread(f1).start();Callable<String> call2 = new MyCallable(200);FutureTask<String> f2 = new FutureTask<>(call2);new Thread(f2).start();//6.获取线程执行完毕后返回的结果//注意:如果执行到这,假如上面的线程还没有执行完毕//这里的代码会暂停,等待上面线程执行完毕后才会获取结果String rs = f1.get();System.out.println(rs);String rs2 = f2.get();System.out.println(rs2 );}
Thread常用方法
public class ThreadTest1 {public static void main(String[] args) {MyThread t1 = new MyThread("1号线程");t1.start();System.out.println(t1.getName());MyThread t2 = new MyThread("2号线程");t2.start();System.out.println(t2.getName());//主线程对象的名字//哪个线程执行它,它就会得到哪个线程对象Thread m = Thread.currentThread();m.setName("最diao的线程");System.out.println(m.getName());for (int i = 1; i <= 5 ; i++) {System.out.println(m.getName() + "线程输出:" + i);}}
}public class MyThread extends Thread {public MyThread(String name) {super(name); //为当前线程设置名字}@Overridepublic void run() {Thread t = Thread.currentThread();for (int i = 1; i <= 3 ; i++) {System.out.println();}}
}
二、线程安全问题
出现原因:
- 存在多个线程同时执行
- 同时访问一个共享资源
- 存在修改该共享资源
取钱案例
需求:小明和小红有一个共同的账户,余额是10万元,模拟2人同时去取钱10万
- 测试类
public class ThreadTest {public static void main(String[] args) {//1.创建一个账户对象,代表两个人的共享账户Account acc = new Account("ICBC-110", 100000);//2.创建两个线程,分别代表小明 小红,再去同一个账户对象中取钱10万new DrawThread(acc,"小明").start();new DrawThread(acc,"小红").start();}
}
- 线程类
public class DrawThread extends Thread{private Account acc;public DrawThread(Account acc,String name) {super(name);this.acc = acc;}@Overridepublic void run() {//取钱acc.drawMoney(100000);}
}
- 账户类
public class Account {private String cardId; //卡后private double money; //账户余额public Account() {}public void drawMoney(double money) {//先搞清楚是谁来取钱String name = Thread.currentThread().getName();//1.判断余额是否够if(this.money >= money){System.out.println(name + "来取钱" + money + "成功!");this.money -= money;System.out.println(name + "取钱后,剩余余额:" + this.money);}else{System.out.println(name + "来取钱,余额不足");}}public Account(String cardId, double money) {this.cardId = cardId;this.money = money;}public String getCardId() {return cardId;}public void setCardId(String cardId) {this.cardId = cardId;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}}
三、线程同步
- 解决线程安全问题的方案
线程同步的思想
- 让多个线程实现先后依次访问共享资源
常见方案
- 加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来
方式1:同步代码块
- 作用:把访问共享资源的核心代码给上锁,保证线程安全
- 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
public void drawMoney(double money) {//先搞清楚是谁来取钱String name = Thread.currentThread().getName();//1.判断余额是否够//this代表共享资源synchronized (this) {if(this.money >= money){System.out.println(name + "来取钱" + money + "成功!");this.money -= money;System.out.println(name + "取钱后,剩余余额:" + this.money);}else{System.out.println(name + "来取钱,余额不足");}}}
方式2:同步方法
- 作用:把访问共享资源的核心方法给上锁
- 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
public synchronized void drawMoney(double money) {}
同步方法底层原理
- 如果方法是实例方法:默认用this作为锁的对象
- 如果方法是静态方法:默认用类名.class作为锁的对象
是同步代码块好还是同步方法好?
- 范围上:同步代码块锁的范围更小,同步方法锁的范围更大
- 可读性:同步方法更好
方式3:Lock锁
- Lock锁是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象
//创建了一个锁对象private final Lock lk = new ReentrantLock();public void drawMoney(double money) {//先搞清楚是谁来取钱String name = Thread.currentThread().getName();try {lk.lock(); //加锁if(this.money >= money){System.out.println(name + "来取钱" + money + "成功!");this.money -= money;System.out.println(name + "取钱后,剩余余额:" + this.money);}else{System.out.println(name + "来取钱,余额不足");}} catch (Exception e) {e.printStackTrace();} finally {lk.unlock();//解锁}}
四、线程池
什么是线程池?
- 一个可以复用线程的技术
不使用线程池的原因
- 创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,严重影响系统的性能
谁代表线程池?
- 代表线程池的接口:ExEcuatorService
如何得到线程池对象?
- 方式1:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
public class ThreadPoolTest {public static void main(String[] args) {
// public ThreadPoolExecutor(int corePoolSize,
// int maximumPoolSize,
// long keepAliveTime,
// TimeUnit unit,
// BlockingQueue<Runnable> workQueue,
// ThreadFactory threadFactory,
// RejectedExecutionHandler handler) {//1.创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());}
}
//2.使用线程池处理Callable任务Future<String> f1 = pool.submit(new MyCallable(100));Future<String> f2 = pool.submit(new MyCallable(200));Future<String> f3 = pool.submit(new MyCallable(300));Future<String> f4 = pool.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());
Executors工具类实现线程池
//1-2 通过Executors创建线程池对象ExecutorService pool = Executors.newFixedThreadPool(3);//核心线程数量到底配置多少呢?//计算密集型的任务:核心线程数量 = CPU的核数 + 1//IO密集型的任务:核心线程数量 = CPU的核数 + 2
五、线程的并发、并行和生命周期
进程
- 正在运行的程序就是一个独立的进程
- 进程中的多个线程其实是并发和并行执行的
并发的含义
- 进程中的线程是由CPU调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能执行,CPU会轮询为系统的每个线程服务
并行的理解
- 在同一个时刻,同时有多个线程在被CPU调度
线程的生命周期
- 也就是线程从生到死的过程,经历的各种状态及状态转换
JAVA线程的状态
- 总共定义6种状态
- 都定义在Thread类的内部枚举类中
public enum State {/*** Thread state for a thread which has not yet started.*/NEW,/*** Thread state for a runnable thread. A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.*/RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>* <li>{@link Object#wait() Object.wait} with no timeout</li>* <li>{@link #join() Thread.join} with no timeout</li>* <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called {@code Object.wait()}* on an object is waiting for another thread to call* {@code Object.notify()} or {@code Object.notifyAll()} on* that object. A thread that has called {@code Thread.join()}* is waiting for a specified thread to terminate.*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>* <li>{@link #sleep Thread.sleep}</li>* <li>{@link Object#wait(long) Object.wait} with timeout</li>* <li>{@link #join(long) Thread.join} with timeout</li>* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>*/TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.*/TERMINATED;}