一、什么是线程?
- 是一个程序内部的一条执行流程
多线程是什么?
- 多条线程由CPU负责调度执行
多线程的创建方式一:继承Thread类
//1.继承Thread类
public class MyThread extends Thread {
//2.必须重写run方法
@Override
public 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 {
@Override
public 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(){
@Override
public void run() {
for (int i = 1; i <= 5 ; i++) {
System.out.println("子线程1输出:" + i);
}
}
};
new Thread(target).start();
//简化形式1:
new Thread(new MyRunnable(){
@Override
public void run() {
for (int i = 1; i <= 5 ; i++) {
System.out.println("子线程2输出:" + i);
}
}
}).start();
//简化形式2
new 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方法
@Override
public 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); //为当前线程设置名字
}
@Override
public 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;
}
@Override
public 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;
}