上节线程基础回顾:
目录:
1.1死锁,如何避免死锁?
1.2线程之间的通信
1.3线程状态
1.4线程池
1.5Lock手动锁
技术细节:
1.1死锁,如何避免死锁?
说明:
奥特曼拥有A锁等待B锁释放,怪兽拥有B锁等待A锁释放,他们相互等待就会形成死循环,造成阻塞,死锁现象
举例:
李某和范某吃饭,饭店活动,每人只有一只筷子,一双筷子才能吃饭
代码演示:
package demo09;
public class BoyRunnable implements Runnable {
@Override
public void run() {
synchronized (LockObject.lockB){
System.out.println("李某获取筷子B");
synchronized (LockObject.lockA){
System.out.println("李某获取筷子A");
System.out.println("李某可以吃饭了");
}
}
}
}
------------------------------
package demo09;
public class GirlRunnable implements Runnable{
@Override
public void run() {
//锁嵌套
synchronized (LockObject.lockA){
System.out.println("范某获得一个筷子A");
synchronized (LockObject.lockB){
System.out.println("范某获得另外一个筷子B");
System.out.println("范某可以吃饭了");
}
}
}
}
---------------------
package demo09;
public class LockObject {
public static Object lockA = new Object();//A筷子
public static Object lockB = new Object();//B筷子
}
---------------------
package demo09;
public class Test {
public static void main(String[] args) {
BoyRunnable boy = new BoyRunnable();
Thread t1 = new Thread(boy);
GirlRunnable girl = new GirlRunnable();
Thread t2 = new Thread(girl);
t1.start();
t2.start();
}
}
输出结果:
总结:
他们都没有吃上饭,他们之间互相等待,所以出现了这种死锁现象
那么如何避免呢?
1.尽量不要出现锁嵌套
2.尽量不使用锁,使用安全类。java.util.concurrent下的类,都属于安全类
3.设置锁的超时时间,Lock锁;到达指定时间拿不到另外一个锁资源,则不会接着等待,抛出异常
4.设置锁休眠时间
代码演示:
package demo09;
public class BoyRunnable implements Runnable {
@Override
public void run() {
//设置休眠时间100单位毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (LockObject.lockB){
System.out.println("李某获取筷子B");
synchronized (LockObject.lockA){
System.out.println("李某获取筷子A");
System.out.println("李某可以吃饭了");
}
}
}
}
------------------------------
package demo09;
public class GirlRunnable implements Runnable{
@Override
public void run() {
//锁嵌套
synchronized (LockObject.lockA){
System.out.println("范某获得一个筷子A");
synchronized (LockObject.lockB){
System.out.println("范某获得另外一个筷子B");
System.out.println("范某可以吃饭了");
}
}
}
}
---------------------
package demo09;
public class LockObject {
public static Object lockA = new Object();//A筷子
public static Object lockB = new Object();//B筷子
}
---------------------
package demo09;
public class Test {
public static void main(String[] args) {
BoyRunnable boy = new BoyRunnable();
Thread t1 = new Thread(boy);
GirlRunnable girl = new GirlRunnable();
Thread t2 = new Thread(girl);
t1.start();
t2.start();
}
}
打印结果:
1.2线程之间的通信
说明:
* 线程交替执行;做到先存钱在取钱;先加后减等等
代码演示:
package demo10;
public class BankCard {
//余额属性
private double blance;
//true表示有钱,false表示没钱
private boolean flag;
//存钱方法
public synchronized void save(double money){
//如果卡里有钱存线程就会进入等待队列。调用wait方法,调用wait方法;这里的wait方法是属于object类里的
if(flag){
try {
//调用wait方法会有异常出现
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
blance = blance + money;
System.out.println(Thread.currentThread().getName()+"往卡中存入1000,卡中余额:"+blance+"元");
flag = true;
//唤醒等待队列的线程
// notify也是object类中的方法
notify();
}
//取钱方法
public synchronized void take(double money){
//如果卡里没有钱取线程就会进入等待队列。调用wait方法,调用wait方法;这里的wait方法是属于object类里的
if(flag==false){
try {
//调用wait方法会有异常出现
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
blance = blance-money;
System.out.println(Thread.currentThread().getName()+"从卡中取出1000,卡中余额:"+blance+"元");
flag = false;
//唤醒等待队列的线程
// notify也是object类中的方法
notify();
}
}
------------------------------------------
package demo10;
public class SaveRunable implements Runnable{
//这里不能new,new的话他们存钱和取钱就不是一张卡了
private BankCard bankCard;
//构造方法,传进来共同的一个卡对象
public SaveRunable(BankCard card){
bankCard = card;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankCard.save(1000);
}
}
}
------------------------------------------
package demo10;
public class TakeRunnable implements Runnable {
//这里不能new,new的话他们存钱和取钱就不是一张卡了
private BankCard bankCard;
//构造方法,传进来共同的一个卡对象
public TakeRunnable(BankCard card){
bankCard = card;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
bankCard.take(1000);
}
}
}
------------------------------------------
package demo10;
public class Test01 {
public static void main(String[] args) {
BankCard bankCard = new BankCard();
SaveRunable saveRunable = new SaveRunable(bankCard);//任务存
TakeRunnable takeRunnable = new TakeRunnable(bankCard);//任务取
Thread t1 = new Thread(saveRunable,"孙悟空");//t1线程执行存钱任务
Thread t2 = new Thread(takeRunnable,"张飞");//t2线程执行取钱任务
t1.start();
t2.start();
}
}
打印结果:
拓展:
*wait sleep 他们有什么区别
1.wait 需要使用notify或notifyAll唤醒,而sleep到时间自动唤醒
2.wait 来自于Object类中,sleep 来自Thread类中
3.wait 会释放锁资源,sleep不会释放锁资源:sleep释放锁资源必须等到休眠时间结束才能释放
4.wait 必须放在同步代码中,而sleep可以放在任意位置
1.3线程状态
图示:
1.4线程池
概念:
原理:
*将任务提交给线程池,由线程池分配任务,运行任务,并在当前任务结束后复用线程
创建线程池:
细节:
* Executor :( 线程池对象 ) 它是线程池的顶级接口,该接口中就存在一个方法;void execute(Runnable command):执行线程任务的方法 * ExecutorService : 它是Executor接口的子接口 * ExecutorService中的方法 * 1.shutdown():关闭线程池---如果当前线程池还有任务,需要等任务完成后才会关闭 * 2.shutdownNow():立刻关闭线程池 * 3.isShutdown():是否属于关闭状态。返回类型是一个boolean值 * 4.isTerminated():判断是否线程池终止了 * 5.submit( Callable<T> task ):执行线程任务的方法 * submit( Runnable task ) * 6.Executors : 它是线程池的工厂类,该类可以获取线程池对象
代码演示:
package demo11;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Executor :( 线程池对象 ) 它是线程池的顶级接口,该接口中就存在一个方法;void execute(Runnable command):执行线程任务的方法
* ExecutorService : 它是Executor接口的子接口
* ExecutorService中的方法
* 1.shutdown():关闭线程池---如果当前线程池还有任务,需要等任务完成后才会关闭
* 2.shutdownNow():立刻关闭线程池
* 3.isShutdown():是否属于关闭状态。返回类型是一个boolean值
* 4.isTerminated():判断是否线程池终止了
* 5.submit( Callable<T> task ):执行线程任务的方法
* submit( Runnable task )
* 6.Executors : 它是线程池的工厂类,该类可以获取线程池对象
*/
public class Test01 {
public static void main(String[] args) {
//newFixedThreadPool创建一个固定个数的线程池对象---alibaba不建议使用Executors创建线程池
// ExecutorService executorService = Executors.newFixedThreadPool(5);
//创建单一线程池---池子中只有一个线程的对象----适合任务的有序执行
// ExecutorService executorService = Executors.newSingleThreadExecutor();
//可变线程池---根据任务的多少自己变化
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 6; i++) {
//submit 在这里也可以使用 ;execute
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~~~~~~~");
}
});
}
//关闭线程池
//executorService.shutdownNow();
}
}
package demo11;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test02 {
public static void main(String[] args) {
//延迟线程池
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
for (int i = 0; i < 100; i++) {
executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("定时执行任务");
}
//TimeUnit.SECONDS 单位秒
},5, TimeUnit.SECONDS);
}
}
}
package demo11;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test03 {
public static void main(String[] args) {
//alibaba 建议自己new线程池----构造函数报错原因
//1.倒包 2.没有无参构造函数 3.构造函数私有化
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);//允许等待的个数
// ThreadPoolExecutor();中的参数
// int corePoolSize,核心线程数
// int maximumPoolSize,最大线程数
// long keepAliveTime,等待时间
// TimeUnit unit,时间单位
// BlockingQueue<Runnable> workQueue:等待队列,等待队列中的个数
ThreadPoolExecutor executorService = new ThreadPoolExecutor(2, 5, 10, TimeUnit.SECONDS, blockingQueue);
for (int i = 0; i < 8; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~~~~");
}
});
}
}
}
拓展:
1.java层的顶层类
object
2.集合的顶层类
collection:单列集合类的根接口
map:键值对集合的根接口
3.异常的顶层类
Throwable
他的下面有两个子类:--Exception
--Error
1.5Lock手动锁
代码演示:
package demo04;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TicketTask implements Runnable{
private int ticket = 100;
//创建一个手动锁
private Lock l = new ReentrantLock();
@Override
public void run() {
while(true) {
l.lock();
try { if (ticket>0){
ticket--;
System.out.println(Thread.currentThread().getName() + " 卖了一张票;剩余: " + ticket+"张");
}else {
break;
}
}finally {
//解锁
//不加上解锁,哪个窗口抢到,哪个窗口直接卖完100张
l.unlock();
}
}
}
}
---------------------
package demo04;
public class Test04 {
public static void main(String[] args) {
TicketTask task = new TicketTask();
Thread t1 = new Thread(task,"窗口A");
Thread t2 = new Thread(task,"窗口B");
Thread t3 = new Thread(task,"窗口C");
Thread t4 = new Thread(task,"窗口D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
synchronized自动锁------Lock手动锁的区别
1. lock手动锁只能给代码块上
2. synchronized自动上锁解锁,lock手动上锁解锁
3. synchronized能在方法和代码块上,上锁。lock只能在代码块上,上锁
4. lock 可以知道是否获得锁资源