线程
1、多线程
1.1 线程与进程
· 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序基本单位;系统运行一个程序即是一个进程从创建,运行到消亡的过程。
· 线程:线程是进程中的一个执行单元,负责当前进程中的执行,一个进程中至少有一个线程。一个进程中是可以多个线程的,这个应用程序也可以称之为多线程程序。
1.2创建线程类
Java 使用 Thread 类代表 线程 ,所有的线程对象都必须是Thread类或其子类的实列。每个线程的作用是完成一定的任务,实际就是执行一段程序流即一段顺序执行的代码
1、定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表线程需要完成的任务,因此把run()方法称为线程执行体。
2、创建Thread子类的实例,即创建了线程对象
3、调用线程对象的start()方法来启动该线程
2、多线程详解
2.1 Thread类
构造方法:
· public Thread() : 分配一个新的线程对象
· public Thread( String name) : 分配一个指定名字的新的线程对象。
· public Thread( Runnable target) : 分配一个带有指定目标的新线程对象。
· public Thread( Runnable target,String name) : 分配一个带有指定目标的新线程对象并指定名字。
常用方法:
· public String getName() : 获取当前线程名称。
· public void start() : 导致此线程开始执行;Java虚拟机调用此线程的run方法。
· public void run() : 此线程要执行的任务在此处定义代码。
· public static void sleep(long millis) : 使用当前正在执行的线程以指定的毫秒数暂停。
· public static Thread currentThread() : 返回对当前正在执行的线程对象的引用
测试类:
自定义线程类:
运行:
2.2 Runnable类
Runnable 也是非常常见的一种,我们只需要重写run方法即可。
1、定义Runnable 接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程执行体。
2、创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Theater对象才是真正的线程对象。
3、调用线程对象的start()方法来启动线程。
代码如下:
3、线程安全
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的
而且其他的变量的值也和预期的是一样的,就是线程安全的。
实体类:
测试类:
运行结果:
1、同样的票数,比如98这张票被卖了两次
2、不存在的票,比如0票-1票,是不存在的
这个问题,几个窗口(线程)票数不同步了,这种问题称为线程安全性问题。
4、线性安全问题解决方法
-- synchronized 锁
· 同步代码块:synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问
对于非static方法,同步锁就是this
对于static方法,我们使用当前方法所在类的字节码对象(类名.Class)
格式:
-- Lock锁
Lock机制提供了比 synchronized 代码块和 synchronized 代码块和 synchronized 方法更广泛的锁定操作,同步代码/同步方法具有的功能
·public void lock() : 加同步锁
·public void unlock() : 释放同步锁
5、线程死锁
1、当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
2、一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁
执行结果:
解决死锁:
(1)减少同步代码块的嵌套
(2)设置锁的超时时间
(3)可以使用安全类jdk提高的安全类
执行结果:
6、线程通信
· 等待:
· public final void wait ()
· public final void wait ( long timeout ) --给wait一个时间,过了时间后自动唤醒
· 必须在对odject加锁的同步代码中,在一个线程中,调用object.wait() 时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在object的等待队列中。释放锁,进入等待队列
· 通知:
· public final void notify() --随机唤醒等待队列中一个线程
. public final void nottifyAll() --唤醒等待队列的所有的线程
package XianCheng.TongXing;
public class BankCard extends Object {
private int balance;
//true:表示卡中有钱,false:卡中没有钱
private boolean flag;
//存钱功能
public synchronized void save(int money) {
//调用了wait 那么当前线程是否有资源,并且进入等待队列中,等待队列中的线程需要其他线程调用notify来唤醒等待队列的线程,参与下次锁的竞争
if(flag==true){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance=balance+money;
System.out.println(Thread.currentThread().getName()+"向卡里面充值了 "+money+" 元;卡里余额有:"+balance);
flag=true;
//唤醒--等待队列中的线程对象
this.notify();
}
//取钱功能
public synchronized void take(int money){
if(flag==false){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance=balance-money;
System.out.println(Thread.currentThread().getName()+"向卡里面取出了 "+money+"元"+"卡里面还剩下:"+balance);
flag=false;
this.notify();
}
}
package XianCheng.TongXing;
public class Sevetask implements Runnable{
private BankCard bankCard;
public Sevetask(BankCard b) {
bankCard=b;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankCard.save(1000);
}
}
}
package XianCheng.TongXing;
public class TakeTask implements Runnable{
private BankCard bankCard;
public TakeTask(BankCard b){
bankCard=b;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankCard.take(1000);
}
}
}
package XianCheng.TongXing;
public class Text {
public static void main(String[] args) {
BankCard bankCard = new BankCard();
Sevetask sevetask = new Sevetask(bankCard);
TakeTask takeTask = new TakeTask(bankCard);
Thread thread1 = new Thread(sevetask,"充钱者A");
Thread thread2 = new Thread(takeTask,"充钱者B");
thread1.start();
thread2.start();
}
}
执行结果:
7、线程状态
NEW:新建状态
RUNNABLE: start()就绪状态-时间片-运行状态. 统称为RUNNABLE
BLOCKED: 堵塞状态。加锁时就如该状态
WAITING: 无期等待: 调用wait方法时会进入该状态
TIMED_WAITING: 有期等待---当调用sleep方法时就会进入该状态
TERMINATED: 终止状态。线程的任务代码执行完毕或出现异常。
线程的状态之间可以通过调用相应的方法,进行转换。
8、线程池
· 问题:
· 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。
· 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。
· 线程池 :
· 线程容器,可设定线程分配的数量上限。
· 将预先创建的线程对象存入池中,并重用线程池中的线程对象
· 避免频繁的创建和销毁。
Executor: 它是线程池的根接口:
void execute(Runnable command):执行Runnable类型的任务。
ExecutorService: 它是Executor的子接口。---
void shutdown():关闭线程池。需要等任务执行完毕。
shutdownNow(); 立即关闭线程池。 不在接受新的任务。
isShutdown(): 判断是否执行了关闭。
isTerminated(): 判断线程池是否终止。表示线程池中的任务都执行完毕,并且线程池关闭了
submit(Callable<T> task);提交任务,可以提交Callable
submit(Runnable task): 提交任务,可以提交Runnable任务
Executors: 它是线程池的工具类,该类提供了创建线程池的一些静态方法
package XianCheng.XianChengChi;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ConCurrent {
public static void main(String[] args) {
//1、创建一个固定长度的线程池
/* ExecutorService executorService = Executors.newFixedThreadPool(5);*/
//2、单一线程池
/*ExecutorService executorService = Executors.newSingleThreadExecutor();*/
//3、可变线程池 -- 缓存线程池
/*ExecutorService executorService = Executors.newCachedThreadPool();*/
//4、延迟线程池
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
/* for(int i=0;i<30;i++){
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"------");
}
});
}*/
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"------");
}
},5, TimeUnit.SECONDS);
executorService.shutdown();
}
}
阿里建议使用原生的模式创建线程池 :
int corePoolSize, 核心线程的个数
int maximumPoolSize, 最多的线程个数
long keepAliveTime, 线程空闲时间。
TimeUnit unit, 空闲的单位