JAVA基础之路——并发(二)

并发(二)

线程状态
线程可以有如下状态:
New(新创建)
Runnable(可运行)
Blocked(被阻塞)
Waiting(等待)
Timed waiting(计时等待)
Terminated(被终止)
要确定一个进程的状态,可以调用getState方法。
在这里插入图片描述

可创建线程
用new创建一个新线程的时候,如new Thread®, 该线程还没开始运行,那么,它的状态就是new(新创建)

Thread t1 = new Thread();
//t1.start();
System.out.println(t1.getState());//调用getState方法获得当前进程状态
/*运行结果是NEW*/

可运行:之差CPU资源(就绪状态)

可运行线程
一旦调用start方法,线程出于Runnable状态,那么他的状态就是Runnable(可运行)状态。
在java中 是不区分运行和可运行状态的。都是Runnanle状态。

Thread t1 = new Thread();
t1.start();
System.out.println(t1.getState());//调用getState方法获得当前进程状态
/*运行结果是RUNNABLE */

被阻塞线程和等待线程
阻塞:比如打印,当你执行了一个打印文件的命令后,因为打印机没开,或者其他的一些外部原因,导致没法打印,这个状态就叫做阻塞状态。
指的是,线程处于被阻塞或者等待状态,原因分一下几种:
–当一个线程视图获取一个内部的对象锁,而该锁被其他线程所拥有的时候,则该线程进入阻塞状态
–当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。
–当有几个方法有一个超时参数,调用他们导致线程进入计时等待状态,这个状态一直保持到超时期满或成功的获得了一个锁的时候。

被终止的线程

线程被终止有一下两个原因:
1:因为run方法正常退出而自然死亡。
2:因为一个没有捕获的异常终止了run方法而意外死亡。

线程属性

线程优先级
调用setPriority方法,提高或者降低任何一个线程的优先级。

Thread t1 = new Thread(r1);
t1.setPriority(1);
Thread t2 = new Thread(r2);
t2.setPriority(5);
t1.start();
t2.start();

守护线程

可以通过调用
t.setDaemon(true);
将线程转换为守护线程。
守护线程的作用就是为其他线程提供服务,当所有线程中只剩下守护线程的时候,虚拟机就退出了,由于只剩下守护线程,就没必要继续运行程序了。

未捕获异常处理器

线程的run方法不能抛出任何受查异常,但是,非受查异常会导致线程终止,这样的话线程就死亡了。
解决方法:在线程死亡之前,异常被传递到一个用于未捕获异常的处理器,
该处理器必须属于一个实现Thread.UncaughtExceotionHandler接口的类,这个接口只有一个方法
Void ybcayghtExceotion(Thread t,Throwable e)
可以用setUncaughtExceptionHandler方法为线程安装一个处理器。
也可以用Thread类的静态方法setDefaultUncaughtExceptionHeandler为所有线程安装一个默认的处理器,
例子:

Runnable r = () -> {
    var i =1/0;//会报出一个算术异常
};
Thread t = new Thread(r);//建立一个线程,调用run方法。
t.setUncaughtExceptionHandler(//异常处理器
        (p,q) -> System.out.println("xxxxxx")
);
t.start();

同步

竞争条件的一个例子

在下面的测试程序中,模拟一个有若干用户的银行,随机的生成在这些账户之间转移钱款的交易。每一个账户有一个线程,每一笔交易中,会从线程所服务的账户中随机转移一定数目的钱款到另一个随即账户中。
代码略 - -
运行结果如下:
在这里插入图片描述
可以看到运行结果出现了错误

多线程之所以出现问题,就是因为多线程没有排他性的使用临界资源和执行临界区代码。

临界资源:多个线程同时操纵的内容(改)
临界区:操纵临界资源的代码段 就被称为临界区。

排他方法:
锁对象:

private Lock bankLock = new ReentrantLock();//new一个锁子,Lock是一个接口。
bankLock.lock();//当某个进程进来后,利用这条语句 锁住进程,其他进程进不来,无法操作。
bankLock.unlock();//解锁,当这个进程运行完毕,一定要进行解锁,换别的进程进来继续执行。

注意:为了防止解锁语句意外不执行,所有要将之放进try捕获异常语句中的finally中,保证它肯定能执行(解锁);

条件:
为了防止出现某一个账户里的钱被取成负数的情况出现,就要加一个条件:
如果要取的钱大于账户剩余的钱,那么就让这个进程先等一等,等别的账户取的钱存到某个随机账户的时候 查一下(就是释放所有进程)看一看是不是存到了自己的这个账户中。如果是,就接着运行进程,如果不是,就继续等待。

private Condition sufficientFunds ;//创建一个条件
sufficientFunds = bankLock.newCondition();//用锁,调用一个条件,条件会维护等待队列
while(accounts[from] < amount)//如果取的钱大于里面账户里剩下的钱
    sufficientFunds.await();//那么 就执行条件 等一等,等别的进程往这个进程里存钱。

.await是进入等待状态,

sufficientFunds.signalAll();//释放所有正在等待的线程。不加ALL的话 是随机挑选一个进程释放(非常危险)。

synchronized关键字
是一个语言层面的锁。
从java1.0开始,所有方法都有一个隐形的内部锁。

public synchronized void method(){
    method body
}

Synchronized相当于调用了一个方法的内部锁。
有了这个关键字 就不需要new锁 和 条件了
用synchronized修饰了之后,
条件由

sufficientFunds.await();

变为:

wait();

释放进程由

sufficientFunds.signalAll();

变为:

notifyAll();

静态方法也可以用声明为synchronized。
其实是把当前这个类的反射给锁住。

问wait()和await(),signalALL()和notifyALL()的区别?
答:await()和notifyALL()是方法中内部锁专用的等待和释放方法。
await()和signalALL()是用自己new出来的锁调用的等待和释放方法。

同步块(同步阻塞)

线程安全的集合
List ArrayList
Set HashSet TreeSet
Nap HashMap HashTree
这些都是以前学的 他们都是线程不安全的集合,但是可以用锁来保护这些进程。但是相对应的,系统开销就很大。
所有可以引用 线程安全的集合 效率会更好。
ConcurrentHashMap

以上所学为个人总结。难免不严谨,见谅!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值