多线程Thread
实现多线程的三种方式
方式一:继承Tread类
package com.itheima.thread.createthread;
/**
* 通过继承Thread类实现多线程
* 主线程应该放在子线程下面,否则本质上还是单线程
* 不能直接调用run方法(线程任务方法),否则会被认为是普通方法,不会产生新线程
* 优点:代码简单
* 缺点:由于单继承的局限性,无法再继承其他类,不便于扩展
*/
public class ThreadDemo01 {
public static void main(String[] args) {
//3.创建一个线程对象
Thread t = new MyThread();
//4.调用start()方法,实际上也调用了run()方法
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程输出:"+i);
}
}
}
//1.定义一个线程类继承Thread类
class MyThread extends Thread {
//2.重写run方法
@Override
public void run(){
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出:"+i);
}
}
}
方式二:实现Runnable接口
package com.itheima.thread.createthread;
/**
* 实现Runnable接口实现多线程
* 优点:可继承类可实现接口,扩展性好
* 缺点:多了一层对象封装,且如果线程有结果是不能返回的
*/
public class ThreadDemo02 {
public static void main(String[] args) {
//3.创建任务对象
Runnable m = new MyRunnable();
//4.将任务对象交给线程对象调用start方法启动新线程
new Thread(m).start();
//匿名内部类简化写法
/* new Thread(new Runnable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程输出:");
}
}
}).start();*/
new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("子线程2输出:"+i);
}
}).start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程输出:"+i);
}
}
}
//1.定义一个线程任务类,实现Runnable接口
class MyRunnable implements Runnable{
//2.重写run方法
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程1输出:"+i);
}
}
}
方式三:实现Callable接口
package com.itheima.thread.createthread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* 通过实现Callable接口,结合FutureTask产生新线程,扩展性好
* Thread对象只能接收Runnable类型对象,FutureTask类实现了Runnable接口,可以交给Thread对象,
* 而实现Callable对象不行,且FutureTask对象可以通过get方法获取线程结束返回的数据
*/
public class ThreadDemo03 {
public static void main(String[] args) {
//3.创建任务对象
Callable m = new MyCallable(10);
//4.将Callable对象封装成FutureTask对象
FutureTask<String> sft = new FutureTask<String>(m);
//5.将FutureTask对象交给Thread对象并调用start方法
new Thread(sft).start();
//3.创建任务对象
Callable m1 = new MyCallable(20);
//4.将Callable对象封装成FutureTask对象
FutureTask<String> sft1 = new FutureTask<String>(m1);
//5.将FutureTask对象交给Thread对象并调用start方法
new Thread(sft1).start();
//6.通过FutureTask对象的get方法获取线程执行结束返回的数据
try {
String s1 = sft.get();
System.out.println("线程一的结果:"+s1);
} catch (Exception e) {
e.printStackTrace();
}
try {
String s2 = sft1.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 {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum+=i;
}
return "1-"+n+"的和为:"+sum;
}
}
Thread类常用API
package com.itheima.thread.api;
/**
* static Thread currentThread() 获取当前线程
* String getName() 获取线程名
* void setName() 设置线程名
* Thread类的构造器:
* public Thread(String name)
* public Thread(Runnable target)
* public Thread(Runnable target,String name)
*/
public class Demo01 {
public static void main(String[] args) {
// Thread m1 = new MyThread();
Thread m1 = new MyThread("线程一");
//获取线程名
System.out.println(m1.getName());
//m1.setName("线程一");
m1.start();
// Thread m2 = new MyThread();
Thread m2 = new MyThread("线程二");
System.out.println(m2.getName());
//m2.setName("线程二");
m2.start();
//获取当前线程,即主线程
Thread t = Thread.currentThread();
//获取主线程名
System.out.println(t.getName());
//设置主线程名
t.setName("主线程");
for (int i = 0; i < 5; i++) {
System.out.println(t.getName()+"输出:"+i);
}
}
}
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
public MyThread() {
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName()+"输出:"+i);
}
}
}
package com.itheima.thread.api;
/**
* static void sleep() 让线程进入休眠状态
*/
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
if (i == 5){
//让主线程休眠3秒
Thread.sleep(3000);
}
System.out.println("主线程输出:"+i);
}
}
}
模拟线程安全问题
测试类
package com.itheima.thread.safe_thread;
/**
* 案例:模拟线程安全问题
* 当多个线程同时访问并修改同一个共享资源时就可能出现线程安全问题
* 模拟:小明和小红拥有一个共同银行账户
* 实现两人同时进入系统取钱,银行血亏
*
*/
public class Case01 {
public static void main(String[] args) {
//定义一个线程类,创建一个账户对象
Account acc = new Account("ABC-111", 100000);
//启动线程,开始取钱
new DrawMoney("小明",acc).start();
new DrawMoney("小红",acc).start();
/*
小红取走100000.0
小红取钱后余额剩余:0.0
小明取走100000.0
小明取钱后余额剩余:-100000.0
*/
}
}
Account类
package com.itheima.thread.safe_thread;
/**
* 账户类
*/
public class Account {
private String cardId;//卡号
private double money;//余额
public void drawMoney(double money){
//获取线程名
String name = Thread.currentThread().getName();
if (this.money>=money){
System.out.println(name+"取走"+money);
//更新余额
this.money-=money;
System.out.println(name+"取钱后余额剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}
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;
}
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
}
DrawMoney线程类
package com.itheima.thread.safe_thread;
/**
* 线程类
*/
public class DrawMoney extends Thread {
private Account account;
public DrawMoney(String name, Account account) {
super(name);
this.account = account;
}
@Override
public void run() {
account.drawMoney(100000);
}
}
解决线程安全问题的方法
一、同步代码块
测试类
package com.itheima.thread.synchronized_thread.code;
/** 同步代码块
使用同步代码块给核心代码上锁,拿到锁对象的对象才能进入,其他对象需等待
锁对象需要对共享该账户的对象都是唯一的,例如可使用字符串"shifan"
字符串在常量池中唯一,但使用任意唯一的锁对象会导致无关代码同样被锁住
即此时其他账户的对象同样无法取钱
故推荐使用共享资源作为锁对象
对于实例方法,应使用this作为锁对象
对于静态方法,应使用字节码(类名.class)作为锁对象
*/
public class Demo01 {
public static void main(String[] args) {
//定义一个线程类,创建一个账户对象
Account acc = new Account("ABC-111", 100000);
//启动线程,开始取钱
new DrawMoney("小明",acc).start();
new DrawMoney("小红",acc).start();
}
}
Account类
package com.itheima.thread.synchronized_thread.code;
/**
* 账户类
*/
public class Account {
private String cardId;//卡号
private double money;//余额
//静态方法推荐使用字节码作为锁对象
public static void run(){
synchronized (Account.class){
}
}
public void drawMoney(double money){
//获取线程名
String name = Thread.currentThread().getName();
//使用同步代码块给核心代码上锁
if (this.money>=money){
System.out.println(name+"取走"+money);
//更新余额
this.money-=money;
System.out.println(name+"取钱后余额剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}
}
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;
}
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
}
线程类
package com.itheima.thread.synchronized_thread.code;
/**
* 线程类
*/
public class DrawMoney extends Thread {
private Account account;
public DrawMoney(String name, Account account) {
super(name);
this.account = account;
}
@Override
public void run() {
account.drawMoney(100000);
}
}
二、同步方法
测试类
package com.itheima.thread.synchronized_thread.method;
/**
* 同步方法
* 使用synchronized修饰可能出现线程安全的方法
* 同步方法底层隐式存在锁对象
* 实例方法默认使用this作为锁对象
* 静态方法默认使用类名.class作为锁对象
*/
public class Demo01 {
public static void main(String[] args) {
//定义一个线程类,创建一个账户对象
Account acc = new Account("ABC-111", 100000);
//启动线程,开始取钱
new DrawMoney("小红",acc).start();
new DrawMoney("小明",acc).start();
}
}
Account类
package com.itheima.thread.synchronized_thread.method;
/**
* 账户类
*/
public class Account {
private String cardId;//卡号
private double money;//余额
//对可能出现线程安全的方法上锁
public synchronized void drawMoney(double money){
//获取线程名
String name = Thread.currentThread().getName();
if (this.money>=money){
System.out.println(name+"取走"+money);
//更新余额
this.money-=money;
System.out.println(name+"取钱后余额剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}
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;
}
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
}
DrawMoney线程类
package com.itheima.thread.synchronized_thread.method;
/**
* 线程类
*/
public class DrawMoney extends Thread {
private Account account;
public DrawMoney(String name, Account account) {
super(name);
this.account = account;
}
@Override
public void run() {
account.drawMoney(100000);
}
}
三、Lock
测试类
package com.itheima.thread.synchronized_thread.lock;
/**
* lock锁,JDK5以后提供,更灵活方便
* 使用Lock接口的实现类ReentrantLock构建锁对象
*/
public class Demo01 {
public static void main(String[] args) {
//定义一个线程类,创建一个账户对象
Account acc = new Account("ABC-111", 100000);
//启动线程,开始取钱
new DrawMoney("小红",acc).start();
new DrawMoney("小明",acc).start();
}
}
Account类
package com.itheima.thread.synchronized_thread.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 账户类
*/
public class Account {
private String cardId;//卡号
private double money;//余额
//使用final修饰,锁对象唯一且不可替换,专业!
private final Lock lock = new ReentrantLock();
public void drawMoney(double money){
//获取线程名
String name = Thread.currentThread().getName();
//上锁
lock.lock();
//使用try-finally防止出现异常时出现死锁
try {
if (this.money>=money){
System.out.println(name+"取走"+money);
//更新余额
this.money-=money;
System.out.println(name+"取钱后余额剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
} finally {
//解锁
lock.unlock();
}
}
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;
}
public Account() {
}
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
}
DrawMoney类
package com.itheima.thread.synchronized_thread.lock;
/**
* 线程类
*/
public class DrawMoney extends Thread {
private Account account;
public DrawMoney(String name, Account account) {
super(name);
this.account = account;
}
@Override
public void run() {
account.drawMoney(100000);
}
}
线程池
ThreadPoolExecutor线程池处理Runnable线程任务
测试类
package com.itheima.thread.threadpool;
import java.util.concurrent.*;
/**
* 线程池
* 处理Runnable任务
* 参数详解:
* 参数一:指定线程池的线程数量(核心线程): corePoolSize 不能小于0
* 参数二:指定线程池可支持的最大线程数: maximumPoolSize 最大数量 >= 核心线程数量
* 参数三:指定临时线程的最大存活时间: keepAliveTime 不能小于0
* 参数四:指定存活时间的单位(秒、分、时、天): unit 时间单位
* 参数五:指定任务队列: workQueue 不能为null
* 参数六:指定用哪个线程工厂创建线程: threadFactory 不能为null
* 参数七:指定线程忙,任务满的时候,新任务来了怎么办: handler 不能为null
* 新任务拒绝策略:
* ThreadPoolExecutor.AbortPolicy 丢弃任务并抛出RejectedExecutionException异常。是默认的策略
* ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法
* ThreadPoolExecutor.DiscardOldestPolicy 抛弃队列中等待最久的任务 然后把当前任务加入队列中
* ThreadPoolExecutor.CallerRunsPolicy 由主线程负责调用任务的run()方法从而绕过线程池直接执行
*/
public class Demo01 {
public static void main(String[] args) {
//定义一个线程池
ExecutorService pool = new ThreadPoolExecutor(
3,5,3,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(5)
, Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//执行任务,三个核心线程
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());
//五个任务在队列中等待
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());
//核心线程全被占用,任务队列已满,创建新的临时线程
pool.execute(new MyThread());
pool.execute(new MyThread());
//临时线程也满了,拒绝策略被触发
//pool.execute(new MyThread());
//线程池关闭,(开发中一般不会用)
// pool.shutdownNow();//立即关闭,即使任务没完成,丢失任务
pool.shutdown();//等待全部线程任务执行完毕再关闭线程池(建议使用)
}
}
ThreadPoolExecutor线程池处理Callable线程任务
测试类
package com.itheima.thread.threadpool;
import java.util.concurrent.*;
/**
* 线程池处理Callable任务
* 使用线程池对象调用submit方法,返回Future对象
* 调用Future对象的get方法获取Callable任务返回的值
*/
public class Demo02 {
public static void main(String[] args) throws Exception {
ExecutorService pool = new ThreadPoolExecutor(3,
5, 3, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),
Executors.privilegedThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
//执行Callable任务,返回结果
System.out.println(pool.submit(new MyCallable(10)).get());
System.out.println(pool.submit(new MyCallable(100)).get());
System.out.println(pool.submit(new MyCallable(20)).get());
System.out.println(pool.submit(new MyCallable(103)).get());
System.out.println(pool.submit(new MyCallable(105)).get());
System.out.println(pool.submit(new MyCallable(150)).get());
System.out.println(pool.submit(new MyCallable(107)).get());
System.out.println(pool.submit(new MyCallable(150)).get());
}
}
使用Executors工具类方法创建线程池
测试类
package com.itheima.thread.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用Executors工具类方法创建线程池对象
* public static ExecutorService newFixedThreadPool(int nThreads)
* 创建指定个数线程的线程池,如果某个线程因为异常结束了,会创建一个新的线程补上
* public static ExecutorService newSingleThreadExecutor()
* 创建只有一个线程的线程池,若因异常结束会创建新的补上
* public static ExecutorService newCachedThreadPool()
* 创建一个线程数量随任务数量动态变化的线程池,若线程空闲一段时间,会被回收
* public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
* 创建一个线程池,可以在给定的延迟后执行任务,或者定期执行任务
*/
public class Demo03 {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());
pool.execute(new MyThread());//此任务排队
}
}
MyThread类
package com.itheima.thread.threadpool;
public class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"输出:"+i);
}
try {
System.out.println("线程"+Thread.currentThread().getName()+"已绑定任务,执行中");
Thread.sleep(10000000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MyCallable类
package com.itheima.thread.threadpool;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum+=i;
}
return Thread.currentThread().getName()+"计算1-"+n+"的和为:"+sum;
}
}
定时器
Timer定时器
package com.itheima.thread.timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
* 定时器
* Timer定时器
* 单线程执行所有任务,若当前任务执行时间过长,会导致后面任务的延迟时间不准确
* 且当有任务出现异常时,线程会直接结束,造成任务丢失
*/
public class Demo01 {
public static void main(String[] args) {
//创建Timer对象
Timer timer = new Timer();
//执行线程任务,延迟两秒,周期两秒
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"输出:A "+new Date());
// try {
// Thread.sleep(20000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(10/0);
}
},2000,2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"输出:B "+new Date());
}
},0,2000);
}
}
ScheduledExecutorService线程池实现定时器
package com.itheima.thread.timer;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* ScheduledExecutorService线程池实现定时器
*/
public class Demo02 {
public static void main(String[] args) {
//创建ScheduledExecutorService线程池,做定时器
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
pool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"输出A "+new Date());
try {
Thread.sleep(50000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},0,2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"输出B "+new Date());
System.out.println(10/0);
}
},0,2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"输出C "+new Date());
}
},0,2, TimeUnit.SECONDS);
}
}
并发与并行
线程的生命周期