一、线程
1、简介
多线程:是指从软硬件上实现多条执行流程的技术。
例子:多用户登录、百度网盘同时上传和下载文件。
2、多线程的创建
主要有三种方式:分别是继承线程类,实现Runnable接口(可以用匿名类实现),实现Callable接口.
缺点方面因为只能继承一个类,不利于功能扩展。
public class ThreadDemo1 {
public static void main(String[] args) {
//3、new一个线程对象
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);
}
}
}
所以需要将子线程放到主线程前,这样才会有多线程。
缺点上即只能跑功能不能返回执行结果,因为是void run()
public class ThreadDemo2 {
public static void main(String[] args) {
//3、创建一个任务对象
Runnable target=new MyRunnable();
//4、把任务对象交给Thread处理(把任务对象交给线程对象处理)
Thread t=new Thread(target);
//5、启动线程
t.start();
for(int i=0;i<10;i++) {
System.out.println("主线程执行输出"+i);
}
}
}
/**
* 1、定义一个线程任务类,实现Runnable接口
*/
class MyRunnable implements Runnable{
/**
* 2、重写run方法,定义线程的执行任务
*/
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<5;i++) {
System.out.println("子线程执行输出"+i);
}
}
}
public class ThreadDemo2Other {
public static void main(String[] args) {
Runnable target=new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println("子线程1执行输出"+i);
}
}
};
Thread t=new Thread(target);
t.start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println("子线程2执行输出"+i);
}
}
}).start();
new Thread(()-> {
for(int i=0;i<10;i++) {
System.out.println("子线程3执行输出"+i);
}
}).start();
for(int i=0;i<10;i++) {
System.out.println("主线程执行输出"+i);
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
//3、创建任务对象
Callable<String> call1=new MyCallable(100);
//4、把Callable任务对象交给FutureTask对象
//FutureTask对象的作用1:是Runnable的对象(实现了Runnable接口),可以交给Thread
//FutureTask对象的作用2:可以在线程执行完毕后通过调用其get方法得到线程执行完成的结果
FutureTask<String> f1=new FutureTask<>(call1);
//5、交给线程处理
Thread t1=new Thread(f1);
//6.启动线程
t1.start();
Callable<String> call2=new MyCallable(200);
FutureTask<String> f2=new FutureTask<>(call2);
Thread t2=new Thread(f2);
t2.start();
try {
//如果f1任务没有执行完毕,这里的代码会等待,直到线程1跑完才提取结果
String rs1=f1.get();
System.out.println("第一个结果"+rs1);
} catch (Exception e) {
e.printStackTrace();
}
try {
//如果f2任务没有执行完毕,这里的代码会等待,直到线程2跑完才提取结果
String rs2=f2.get();
System.out.println("第二个结果"+rs2);
} 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 "子线程执行结果:"+sum;
}
}
3、Thread的常用方法
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
//为当前线程对象设置名称,送给父类的有参数构造器
super(name);
}
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"线程输出:"+i);
}
}
}
public class ThreadDemo1 {
//main方法是由主线程负责调度的
public static void main(String[] args) {
Thread t1=new MyThread("1号");
//t1.setName("1号");
t1.start();
System.out.println(t1.getName());
Thread t2=new MyThread("2号");
//t2.setName("2号");
t2.start();
System.out.println(t2.getName());
//哪个线程执行它,它就得到哪个线程对象
//主线程名称就叫main
Thread m=Thread.currentThread();
System.out.println(m.getName());
m.setName("最牛线程");
for (int i = 0; i < 5; i++) {
System.out.println(m.getName()+"线程输出:"+i);
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) throws Exception {
for(int i=1;i<=5;i++) {
System.out.println("输出:"+i);
if(i==3) {
//让线程进入休眠状态
Thread.sleep(3000);
}
}
}
}
4、线程安全
线程安全问题:多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。
public class Account {
private String cardId;
private double money;//账户余额
public Account() {
}
public Account(String cardId,double money) {
this.cardId=cardId;
this.money=money;
}
public void drawMoney(double money) {
//0.先获取是谁来取钱
String name=Thread.currentThread().getName();
//1、判断账户是否共享
//同步代码块
//this==acc共享账户
synchronized (this) {
if(this.money>=money) {
//2、取钱
System.out.println(name+"来取钱:"+money);
//3、更新余额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else {
//4、余额不足
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 static void run() {
synchronized (Account.class) {//静态方法中同步代码块的锁对象形式
}
}
}
/**
取钱的线程类
*/
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 ThreadDemo {
public static void main(String[] args) {
//1、定义线程类,创建一个共享的账户对象
Account acc=new Account("ICBC-111",100000);
//2、创建2个线程对象,代表小明和小红同时进来了
new DrawThread(acc,"小明").start();
new DrawThread(acc,"小红").start();
}
}
5、线程同步
为了解决线程安全问题。
取钱案例出现问题的原因:多个线程同时执行,发现账户都是够钱的。
如何才能保证线程安全:让多个线程实现先后依次访问共享资源。
线程同步的核心思想: 加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来。
1)方法
同步代码块
同步方法
同步代码块锁的范围更小,同步方法锁的范围更大
lock锁
private final Lock lock=new ReentrantLock();
public void drawMoney(double money) {
//0.先获取是谁来取钱
String name=Thread.currentThread().getName();
//1、判断账户是否共享
lock.lock(); //上锁
try {
if(this.money>=money) {
//2、取钱
System.out.println(name+"来取钱:"+money);
//3、更新余额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else {
//4、余额不足
System.out.println(name+"取钱余额不足");
}
}finally {
lock.unlock();//解锁,用try...finally保证一定能解锁
}
}
6、线程通信
线程通信的前提:线程通信通常是在多个线程操作同一个共享资源的时候需要进行通信,且要保证线程安全。
7、线程池【重点】
1)线程池概述
可以看作服务员服务客人的模式。
2)线程池实现的API、参数说明
其中线程池中的非核心线程可以看作是临时工,核心线程是正式工,不会消失。
3)线程池处理Runnable任务
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"输出:Helloword"+i);
}
try {
System.out.println(Thread.currentThread().getName()+"本任务与线程绑定,线程进入休眠了!");
Thread.sleep(1000000); //即占用该线程
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
/**
目标:自定义一个线程池对象,并测试其特性
*/
public class ThreadPoolDemo1 {
public static void main(String[] args) {
// 1、创建线程池对象
ExecutorService pool=new ThreadPoolExecutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//2、给任务线程池处理
Runnable target=new MyRunnable();
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//创建临时线程(因为核心线程和任务队列都满了才创建临时线程)
pool.execute(target);
pool.execute(target);
//不创建,拒绝策略被触发
pool.execute(target);
//关闭线程池(开发中一般不会使用)
pool.shutdownNow();//立即关闭,即使任务没有完成
//pool.shutdown();//会等待任务完成后关闭
}
}
4)线程池处理Callable任务
FutureTask是Future(父类)的实现类对象
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()+"执行结果:"+sum;
}
}
public class ThreadPoolDemo2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 1、创建线程池对象
ExecutorService pool=new ThreadPoolExecutor(3,5,6,TimeUnit.SECONDS,new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//2、给任务线程池处理
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));
Future<String> f5= pool.submit(new MyCallable(500));
//String rs=f1.get();
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
System.out.println(f4.get());
System.out.println(f5.get());
}
}
5)Executors工具类实现线程池
public class ThreadPoolDemo3 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 1、创建固定线程数据的线程池
ExecutorService pool=Executors.newFixedThreadPool(3);
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());//已经没有多余的线程了
}
}
8、定时器
public class TimerDemo2 {
public static void main(String[] args) {
//1、创建ScheduledExecutorService线程池,做定时器
ScheduledExecutorService pool=Executors.newScheduledThreadPool(3);
//2、开启定时任务
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+"执行输出aaa"+new Date());
try {
Thread.sleep(10000);
} catch (Exception e) {
// TODO: handle exception
}
}
}, 0, 2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+"执行输出bbb"+new Date());
}
}, 0, 2, TimeUnit.SECONDS);
}
}
9、并发、并行
10、线程的生命周期