1.第三种创建线程的方法
通过实现Callable重写call方法来创建线程
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum=0;
//案例100以内的和
for (int i = 0; i <= 100; i++) {
sum=sum+i;
}
return sum;
}
}
创建线程时发现,无法传入Callable,可以创建了一个Future对象,传入了Callable实例,返回值传递给线程构造方法
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();//创建MyCallable对象实例
FutureTask<Integer> integerFuture = new FutureTask<>(myCallable);//创建FutureTask对象,将MyCallable对象作为参数传递给构造函数
Thread thread = new Thread(integerFuture);//创建线程
thread.start();//启动
System.out.println(integerFuture.get());//运行结果: 5050
}
}
也可以用到下面的线程池来执行。
2.死锁
两个或多个以上线程彼此拥有对方所需要的锁标记而都不释放已有的锁,比如线程A拥有A锁,等待获取B锁,线程B拥有B锁,等待获取A锁。就会造成死锁
例子:小A和小B都需要集齐画笔和画板才能画画
public class Draw {
public static Object board=new Object(); //画笔
public static Object brush=new Object(); //画板
}
public class XiaoA extends Thread{
@Override
public void run() {
synchronized (Draw.board){
System.out.println(this.getName()+"获取了画笔");
synchronized (Draw.brush){
System.out.println(this.getName()+"获取了画板");
System.out.println(this.getName()+"画完了");
}
}
}
}
public class XiaoB extends Thread{
@Override
public void run() {
synchronized (Draw.brush){
System.out.println(this.getName()+"获取了画板");
synchronized (Draw.board){
System.out.println(this.getName()+"获取了画笔");
System.out.println(this.getName()+"画完了");
}
}
}
}
public class DrawTest {
public static void main(String[] args) throws InterruptedException {
XiaoA xiaoA = new XiaoA();
XiaoB xiaoB = new XiaoB();
xiaoA.setName("小A");
xiaoB.setName("小B");
xiaoA.start();
xiaoB.start();
}
}
运行结果:
发现并没有运行完,但代码执行不下去了,这就形成了死锁
如何解决死锁:1减少同步代码的嵌套2.设置锁的超时时间3.使用安全类-jdk提供的安全类
这里通过设置小B休眠来让小A先获取画笔和画板,然后释放锁,再由小B获取
public static void main(String[] args) throws InterruptedException {
XiaoA xiaoA = new XiaoA();
XiaoB xiaoB = new XiaoB();
xiaoA.setName("小A");
xiaoB.setName("小B");
xiaoA.start();
Thread.sleep(100);//设置小B休眠100毫秒
xiaoB.start();
}
}
3.线程通信
通过wait()等待和notify()通知可以使线程之间互相通信,可以使线程之间能够有序的执行
public final void wait()等待
public final void wait(long timeout) 等待的时长
public final void notify()通知等待队列的一个线程
public final void notifyAll()通知等待队列的所有线程
案例:小A往银行卡里充钱,小B往银行卡里取钱,条件:银行卡里没钱小A才能充钱,银行卡有钱小B才能取钱
/**
* 银行卡
*/
public class BankCard {
//余额
private static int balance;
//标记 true表示卡里有钱,false表示卡里没钱
private static boolean flag;
/**
* 存钱
* @param money 存的金额
*/
public synchronized void store(int money){
if (flag){
try {
this.wait();//如果有钱进行等待,释放锁进入等待队列
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance+=money;
System.out.println(Thread.currentThread().getName()+"存入了"+money+"剩余"+balance);
flag=true;
this.notify();//通知等待队列
}
/**
* 取钱
* @param money 取的金额
*/
public synchronized void take(int money){
if (!flag){
try {
this.wait();//如果没钱进行等待,释放锁进入等待队列
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance-=money;
System.out.println(Thread.currentThread().getName()+"取出了"+money+"剩余"+balance);
flag=false;
this.notify();//通知等待队列
}
}
public class StoreRunnable implements Runnable{
private BankCard bankCard;
public StoreRunnable(BankCard bank){
bankCard=bank;
}
@Override
public void run() {
//存5次
for (int i = 0; i < 5; i++) {
bankCard.store(1000);
}
}
}
public class TackRunnable implements Runnable{
private BankCard bankCard;
public TackRunnable(BankCard bank){
bankCard=bank;
}
@Override
public void run() {
//取5次
for (int i = 0; i < 5; i++) {
bankCard.take(1000);
}
}
}
public class CardTest {
public static void main(String[] args) {
BankCard bankCard = new BankCard();
StoreRunnable storeRunnable = new StoreRunnable(bankCard);//存钱
TackRunnable tackRunnable = new TackRunnable(bankCard);//取钱
new Thread(storeRunnable,"小A").start();
new Thread(tackRunnable,"小B").start();
}
}
运行结果:
小A存入了1000剩余1000
小B取出了1000剩余0
小A存入了1000剩余1000
小B取出了1000剩余0
小A存入了1000剩余1000
小B取出了1000剩余0
小A存入了1000剩余1000
小B取出了1000剩余0
小A存入了1000剩余1000
小B取出了1000剩余0
Process finished with exit code 0
sleep和wait的区别
(1)来自不同的类:sleep来自Thread,wait来自Object(2)是否释放锁资源:sleep不会释放锁资源。wait会释放锁资源(3)用法:sleep时间到了自动会醒,而wait需要调用notify或notifyAll方法唤醒。
4.线程的状态
NEW:新建状态
RUNNABLE:由分为就绪状态和运行状态:start()为就绪状态,就绪状态到时间片执行时为运行状态为运行状态,统称RUNNABLE状态
BLOCKED:堵塞状态。加锁时就为该状态
WAITING:无期等待:调用wait方法时会进入该状态
TIMED_WAITING:有期等待:调用sleep方法就会进入该状态
TERMINATED:终止状态:线程的温度执行完毕或者出现异常
5.线程池
是线程的容器,用来管理、分配和复用线程,避免频繁的创建和销毁
线程池的创建:可以通过Executors线程池的工具类来创建线程池
1.创建一个固定长度的线程池
ExecutorService executorService=Executors.newFixedThreadPool(5);
ExecutorService executorService = Executors.newFixedThreadPool(5);//创建线程固定数量为5的线程池
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了");
}
});
}
运行结果:线程数量只有5
pool-1-thread-1执行了
pool-1-thread-2执行了
pool-1-thread-1执行了
pool-1-thread-1执行了
pool-1-thread-1执行了
pool-1-thread-1执行了
pool-1-thread-3执行了
pool-1-thread-4执行了
pool-1-thread-2执行了
pool-1-thread-5执行了
2.创建一个单一线程池
ExecutorService executorService=Executors.newSingleThreadExecutor();
ExecutorService executorService = Executors.newSingleThreadExecutor();//创建单一线程池
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了");
}
});
}
3.可变线程池,会自动调整大小
ExecutorService executorService=Executor.newCachedThreadPool();
ExecutorService executorService = Executors.newCachedThreadPool();//可变线程池
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了");
}
});
}
4.延迟线程池
ScheduledExecutorService executorService=Executors.newScheduledThreadPool(5);
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);//核心线程数
for (int i = 0; i < 10; i++) {
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了");
}
},10, TimeUnit.SECONDS); //10秒
}
发现在运行10秒后才执行
5.自定义线程池(推荐)
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>()等待队列 ---暂存区
ExecutorService executor=new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,unit,workQueue);
corePoolSize:核心线程数
maxPoolSize:最大线程数
keepAliveTime:线程闲置时间
unit:时间单位
workQueue:等待队列
threadFactory:表示线程池中工作线程的线程工厂,用于创建线程
handler:拒绝策略,当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时,对任务的拒绝方式。
int corePoolSize=5;//核心线程数
int maxPoolSize=8;//最大线程数
int keepAliceTime=10;//线程闲置时间
TimeUnit unit=TimeUnit.SECONDS; //秒
LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>(5);//等待队列
ThreadPoolExecutor executorService = new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliceTime,unit,workQueue);
6.线程池常用方法
Executor: 是线程池的根接口
void execute(Runnable command)提交Runnable类型的任务给线程池执行,无返回值
ExecutorService:是Executor的子接口
submit(Callable<T> task) 和 submit(Runnable task) 提交一个Callable或Runnable任务给线程池执行
shutdown():平缓的关闭线程池,不在接受新的任务提交,但会等待已提交的任务完成后关闭
//测试10次任务
executorService.shutdown();
//关闭线程池但会等待任务执行完毕
运行结果:
pool-1-thread-1执行了
pool-1-thread-2执行了
pool-1-thread-5执行了
pool-1-thread-2执行了
pool-1-thread-3执行了
pool-1-thread-3执行了
pool-1-thread-1执行了
pool-1-thread-2执行了
pool-1-thread-5执行了
pool-1-thread-4执行了
shutdownNow():立即停止所有正在执行的任务,并返回等待执行的任务列表
//测试10次任务
executorService.shutdownNow();
//立即关闭了线程池,剩余的5次没有执行完
pool-1-thread-1执行了
pool-1-thread-4执行了
pool-1-thread-2执行了
pool-1-thread-3执行了
pool-1-thread-5执行了
isShutdown():判断线程池是否已关闭
isTerminated():判断线程池是否终止,判断线程池的任务是否已经完成
ScheduledExecutorService:执行定时任务
schedule(Runnable command,long delay,TimeUnit unit)传Runnable类型,在指定的延迟时间后执行一次任务
schedule(Callable<V> callable,long delay,TimeUnit unit)传Callable类型,在指定延迟时间后执行一次有返回值的任务