一.创建多线程方式
1.采用extends Thread方法实现
优点:编码简单
缺点:线程类已经继承
Thread
,无法继承其他类,不利于扩展。
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//获取线程名字
String name = getName();
System.out.println(name + "下载进度" + i + "%");
}
}
}
/**
* 目标:采用extends Thread方法实现线程
* 步骤如下:
* 第一步: 写一个MyThread类继承Thread类,复写run方法
* 第二步: 创建MyThread类的对象
* 第三步: 调用start方法,启动线程
*/
public class Demo {
public static void main(String[] args) {
//创建MyThread对象
MyThread t1 = new MyThread();
//调用start方法启动线程
t1.start();
MyThread t2 = new MyThread();
t2.start();
}
}
2.采用implements Runnable 接口方式创建线程
优点:线程任务类只是实现了
Runnale
接口,可以继续继承和实现。
步骤如下:
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i <100; i++) {
//可以使用Thread的静态方法currentThread()获取名字
Thread t = Thread.currentThread();
String name = t.getName();
System.out.println(name+"下载进度" + i + "%");
}
}
}
/**
* 目标:采用implements Runnable接口方式创建线程
*
* 解释:Runnable接口的实现类,我们可以把它看做线程任务。该实现方式,就是将线程与线程任务剥离出来。
*
* 步骤如下:
* 第一步: 写一个MyRunnable类实现Runnable接口,复写run方法
* 第二步: 创建MyRunnable类的对象
* 第三步: 创建Thread对象,将MyRunnable对象作为参数传递进来
* 第四步: 调用start方法,启动线程。
*/
public class Demo {
public static void main(String[] args) {
//创建MyRunnable类的对象
MyRunnable mr = new MyRunnable();
//创建Thread对象,将MyRunnable对象作为参数传递进来
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.start();
t2.start();
}
}
3.采用implements Callable接口方式创建线程
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
缺点:编码复杂一点。
步骤如下:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
}
**
* 目标:采用implements Callable接口方式创建线程。
* <p>
* 步骤如下:
* 第一步: 写一个MyCallable类实现Callable接口,复写call方法
* 第二步: 创建MyCallable类的对象
* 第三步: 创建FutureTask对象,将MyCallable对象作为参数封装进来
* 第四步: 创建Thread对象,将FutureTask对象作为参数封装进来
* 第五步: 调用start方法,启动线程。
*/
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建MyCallable类的对象
MyCallable mc = new MyCallable();
// 创建FutureTask对象,将MyCallable对象作为参数封装进来
FutureTask<Integer> task = new FutureTask<>(mc);
//创建Thread对象,将FutureTask对象作为参数封装进来
Thread t1 = new Thread(task);
t1.start();
//用FutureTask中get来获取方法中的返回值
Integer rs = task.get();
System.out.println(rs);
}
}
二.Thread常用方法
Thread常用方法:获取线程名称getName()、设置名称setName()、获取当前线程对象currentThread() 、sleep(long time)休眠时间单位毫秒
三.线程安全
多个线程操作同一个共享资源时可能会出现业务安全问题,称为线路安全问题
代码演示如下
/*
需求:小明和小红是一对夫妻有一个共同账户有余额10万元钱,使用多线程模拟小明和小红同时来取钱的过程。
*/
public class Demo {
public static void main(String[] args) {
Account account = new Account();
DrawThread t1 = new DrawThread(account);
t1.setName("小明");
DrawThread t2 = new DrawThread(account);
t2.setName("小红");
t1.start();
t2.start();
}
}
public class Account {
private int money = 100000;
public void drawMoney(int money) {
String name = Thread.currentThread().getName();
if (this.money >= money) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.money -= money;
System.out.println(name + "取走了" + money + "还剩" + this.money);
} else {
System.out.println("余额不足!");
}
}
}
public class DrawThread extends Thread{
private Account account;
public DrawThread( Account account){
this.account=account;
}
@Override
public void run() {
account.drawMoney(100000);
}
}
四.线程同步
1.解决线程安全问题
(1)synchronized(同步锁对象){操作共享资源的核心代码}
加锁:每次只能让一个线程访问共享数据,其他线程在外等待。
//取钱
public void drawMoney(int money) {
//先获取当前的线程名称
String name = Thread.currentThread().getName();
//练习: 给下面的代码加上同步代码块 synchronized(同步锁对象){操作共享资源的核心代码}
//同步锁对象要求
/**
* 规范上:建议使用共享资源作为锁对象。
* 对于实例方法建议使用this作为锁对象。
* 对于静态方法建议使用字节码(类名.class)对象作为锁对象
*/
synchronized (this) {
if (this.money >= money) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.money -= money;
System.out.println(name + "来取钱了,取了" + money + "元,还剩" + this.money + "元");
} else {
//如果钱不够,打印输出余额不足.
System.out.println(name + "余额不足");
}
}
}
(2)同步方法
* 方法二 同步方法 * 1.同步方法是有锁对象的 只是没有显示出来 * 2.如果是实例方法锁对象是this * 3.如果是静态方法锁对象是类的字节码 类名.class * 修饰符 synchronized 返回值类型 方法名称(形参列表) {操作共享资源的代码}
public synchronized void drawMoney(int money) {
//先获取当前的线程名称
String name = Thread.currentThread().getName();
//练习: 给下面的代码加上同步代码块
if (this.money >= money) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.money -= money;
System.out.println(name + "来取钱了,取了" + money + "元,还剩" + this.money + "元");
} else {
//如果钱不够,打印输出余额不足.
System.out.println(name + "余额不足");
}
(3)lock锁
Lock lock = new ReentrantLock()定义锁对象 lock.lock();//上锁 lock.unlock();//释放锁
五,线程池
ThreadPoolExecutor实现线程池对象,通过Runnable接口实现
1.线程池就是一个可以复用线程的技术。
2. 核心线程都在忙 + 任务队列满了,就会创建临时线程
3.核心线程都在忙 + 临时线程也忙 + 任务队列也满了,多余的任务就会触发拒绝策略
*
* 目标:使用ThreadPoolExecutor类去创建线程池,并使用submit方法提交Runnable任务
* 从 API看构造方法的参数:
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
threadFactory - 执行程序创建新线程时使用的工厂。
handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
*/
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//核心线程数 corePoolSize
5,//允许最大线程数 maximumPoolSize
2,//临时线程存活时间 keepAliveTime
TimeUnit.SECONDS,//时间单位 unit - keepAliveTime
new ArrayBlockingQueue<>(10),//任务队列 workQueue
Executors.defaultThreadFactory(),//获取默认的线程工厂 threadFactory
new ThreadPoolExecutor.AbortPolicy()//拒绝策略 handler
);
//拒绝策略触发时机=任务量>最大线程数+任务队列长度
//执行Runnable1
for (int i = 0; i < 15; i++) {
int j=i;
pool.execute(new Runnable() {
@Override
public void run() {
//获取线程名称
String name = Thread.currentThread().getName();
System.out.println(name+"个任务在执行"+j+"号任务");
}
});
}
//关闭线程池
pool.shutdown();
(2)
使用ThreadPoolExecutor创建线程池,并提交Callable任务。
* 目标:使用ThreadPoolExecutor创建线程池,并提交Callable任务。
*/
public class Demo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
4,//核心线程数 corePoolSize
5,//允许最大线程数 maximumPoolSize
2,//临时线程存活时间 keepAliveTime
TimeUnit.SECONDS,//时间单位 unit - keepAliveTime
new ArrayBlockingQueue<>(10),//任务队列 workQueue
Executors.defaultThreadFactory(),//获取默认的线程工厂 threadFactory
new ThreadPoolExecutor.AbortPolicy()//拒绝策略 handler
);
for (int i = 0; i < 16; i++) {
Future<Integer> f = pool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
String name = Thread.currentThread().getName();
System.out.println(name + "执行了!");
return sum;
}
});
Integer rs = f.get();
System.out.println(rs);
}
pool.shutdown();
}