目录
1.死锁以及如何避免死锁
①死锁
- 当线程A持有独占锁a,并尝试去获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁,而发生的阻塞现象,称之为死锁。
- 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,因此可能会造成死锁。
②如何避免死锁
- 尽量使用tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock, ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
- 尽量使用Java.util.concurrent下的并发类 代替自己手写锁。
- 尽量降低锁的使用力度,尽量不要几个功能用同一把锁。
- 尽量减少同步的代码块的嵌套。
public class BoyTask extends Thread{
@Override
public void run() {
synchronized (LockObject.lockA){
System.out.println("男孩获得画板");
synchronized (LockObject.lockB){
System.out.println("男孩获得画笔");
System.out.println("男孩可以画画了");
}
}
}
}
public class GirlTask extends Thread{
@Override
public void run() {
synchronized (LockObject.lockB){
System.out.println("女孩获得画笔");
synchronized (LockObject.lockA){
System.out.println("女孩获得画板");
System.out.println("女孩可以画画了");
}
}
}
}
public class LockObject {
public static Object lockA = new Object();
public static Object lockB = new Object();
}
public class Test07 {
public static void main(String[] args) {
BoyTask boyTask = new BoyTask();
GirlTask girlTask = new GirlTask();
boyTask.start();
girlTask.start();
}
}
2.线程通信
sleep()和wait()的区别:
- 来自不同的类:wait来自于Object类中,sleep来自Thread中。
- 作用位置不同:wait必须放在同步代码块中,而sleep可以放在任意位置。
- 是否会释放锁资源:wait会释放锁资源,sleep不会释放锁资源。
- 用法:wait需要notify或notifyAll唤醒,而sleep到时间自动唤醒。
notify()和notifyAll()的区别:
- notify随机唤醒等待对队列中的一个线程,notifyAll会唤醒等待队列中的所有线程。
- notifyAll()调用后,会将全部想成由等待池移到锁池,然后参与锁的竞争,竞争成功后则继续执行,如果不成功则留在锁池中等待被释放后再次参与竞争。而notify()只会唤醒一个线程,具体唤醒哪一个线程一虚拟机控制。
public class BankCard {
private int balance;
//当flag为true时表示卡中有钱,为false时表示卡中没钱
private boolean flag;
//存钱
public synchronized void save(int money){
if (flag){
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance+=money;
System.out.println(Thread.currentThread().getName()+"往卡中存了"+money+"元,卡中剩余"+balance+"元");
flag=true;
notify();
}
//取钱
public synchronized void take(int money){
if(flag==false){
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance-=money;
System.out.println(Thread.currentThread().getName()+"从卡中取了"+money+"元,卡中剩余"+balance+"元");
flag=false;
notify();
}
}
public class BankCard {
private int balance;
//当flag为true时表示卡中有钱,为false时表示卡中没钱
private boolean flag;
//存钱
public synchronized void save(int money){
if (flag){
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance+=money;
System.out.println(Thread.currentThread().getName()+"往卡中存了"+money+"元,卡中剩余"+balance+"元");
flag=true;
notify();
}
//取钱
public synchronized void take(int money){
if(flag==false){
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
balance-=money;
System.out.println(Thread.currentThread().getName()+"从卡中取了"+money+"元,卡中剩余"+balance+"元");
flag=false;
notify();
}
}
public class SaveTask implements Runnable{
private BankCard bankCard;
public SaveTask(BankCard bc){
bankCard=bc;
}
@Override
public void run() {
for (int i = 0; i <5; i++) {
bankCard.save(798);
}
}
}
public class TakeTask implements Runnable{
private BankCard bankCard;
public TakeTask(BankCard bc){
bankCard=bc;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
bankCard.take(798);
}
}
}
public class Test08 {
public static void main(String[] args) {
BankCard bankCard = new BankCard();
SaveTask saveTask = new SaveTask(bankCard);
TakeTask takeTask = new TakeTask(bankCard);
Thread t1 = new Thread(saveTask,"father");
Thread t2 = new Thread(takeTask,"son");
t1.start();
t2.start();
}
}
3.线程的状态
新建(New):当线程对象被创建但尚未启动时处于新建状态。
可运行/就绪(Runnable):线程对象调用了status()方法后,线程处于可运行状态。在可运行状态下,线程可能正在执行,也可能正在等待系统资源(如处理器时间片)。
运行(Running):线程获得了处理器时间片正在执行其任务。
阻塞(Blocked):线程被阻塞并暂时停止执行,通常是因为等待某个操作的完成(如等待I/O操作、等待获取锁、等待某个条件满足等)。
等待(Waiting):线程等待某个特定条件的发生,需要其他线程显式地唤醒(如通过wait()方法)。
超时等待(Timed Waiting):线程等待一段特定时间,超过时间后会自动唤醒。
终止(Terminated):线程执行完毕或出现异常导致终止,不再可运行。
4. 线程池
4.1 如何创建线程池
(1)线程池的种类:
固定长度的线程池
单一线程池
可变线程池
延迟线程池
(2)线程池中参数的意思:
- core: 核心线程数
- max: 最大线程池数
- long: 等待时长
- unit: 等待单位
- BlockQueue:等待队列对象。
(3)Executor: 它是线程池的根接口:
- void execute(Runnable command):执行Runnable类型的任务。
(4) ExecutorService: Executor的子接口。
- void shutdown():关闭线程池。需要等任务执行完毕。
- shutdownNow(); 立即关闭线程池。 不会再接受新的任务。
- isShutdown(): 判断是否执行了关闭。
- isTerminated(): 判断线程池是否终止。表示线程池中的任务都执行完毕,线程池关闭。
- submit(Callable<T> task);提交任务,可以提交Callable。
- submit(Runnable task): 提交任务,可以提交Runnable。
(5)Executors: 线程池的工具类,该类提供了创建线程池的一些静态方法。
以下是通过通过Executors工具类创建线程池:
4.1.1固定长度线程池
public class Test09 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~");
}
});
}
executorService.shutdown();
// executorService.shutdownNow();
}
}
4.1.2单一线程池
class Test10{
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~");
}
});
}
}
}
4.1.3可变线程池
class Test11{
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~");
}
});
}
executorService.shutdown();
}
}
4.1.4延迟线程池
class Test12{
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
for (int i = 0; i < 10; i++) {
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~");
}
},5,TimeUnit.SECONDS);
}
executorService.shutdown();
}
}
4.2原生模式创建线程池
class Test13{
public static void main(String[] args) {
//capacity:5 最多有五个任务在等待
BlockingQueue<Runnable> workQueye=new ArrayBlockingQueue<>(5);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, workQueye);
for (int i = 0; i < 20; i++) {
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~");
}
});
}
}
}
5. 补充(实现Callable接口创建线程 )
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//①创建线程对象
MyCallable myCallable = new MyCallable();
//把线程任务封装到FuntureTask类中,该类可以获取线程任务执行后的结果.
FutureTask<Integer> integerFutureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(integerFutureTask);
thread.start();
System.out.println(integerFutureTask.get());
//②使用线程池来执行任务
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Integer> submit = executorService.submit(new MyCallable());
System.out.println(submit.get());
executorService.shutdown();
}
}
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;
}
}