JAVA多线程
多线程概念
简单介绍:多线程是CPU同时处理多个任务
现实场景:人边吃饭边玩手机,属于多线程的范畴
进程和线程
简单介绍:进程包含线程
实际场景:看视频时,视频播放,声音播放,字幕加载
进程由系统分配,线程则是实际的功能执行
代码中模拟的多线程,他是由单核CPU执行只是切换的很块,每个一点点执行,看起来像是同时执行。
真正的多线程是指,多CPU,既多核执行
多线程业务场景
实际场景:当秒杀商品时,就是多个线程在执行,多线程互相争抢资源,就需要给他们排队,来控制资源的抢夺。
多线程创建方式
继承Thread类
注意点:线程启动之后并不会立即执行,将会由CPU调度执行
package Thread;
/**
* @className: ThreadTest1
* @description:
* @createDate: 2021年06月25日 11:11:22
* @author: ns
*/
public class ThreadTest1 extends Thread {
public static void main(String[] args) {
ThreadTest1 threadTest1 = new ThreadTest1();
threadTest1.start();
for (int i = 0; i < 2000; i++) {
System.out.println("看代码" + i);
}
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("学习多线程代码" + i);
}
}
}
运行效果
使用多线程实现多文件下载
包下载路径
https://search.maven.org/artifact/org.apache.directory.studio/org.apache.commons.io/2.4/jar
package Thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* @className: ThreadTest2
* @description:
* @createDate: 2021年06月25日 14:08:56
* @author: ns
*/
public class ThreadTest2 extends Thread {
private String url;
private String name;
public ThreadTest2(String url, String name) {
this.url = url;
this.name = name;
}
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url, name);
System.out.println("下载了文件名为" + name);
}
public static void main(String[] args) {
ThreadTest2 threadTest1 = new ThreadTest2("https://img-blog.csdnimg.cn/2021061516385914.png#pic_center", "图1.png");
ThreadTest2 threadTest2 = new ThreadTest2("https://img-blog.csdnimg.cn/20210615175631519.png#pic_center", "图2.png");
ThreadTest2 threadTest3 = new ThreadTest2("https://img-blog.csdnimg.cn/20210616110215957.png#pic_center", "图3.png");
threadTest1.start();
threadTest2.start();
threadTest3.start();
}
}
class WebDownLoader {
public void downLoader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常");
}
}
}
实现Runnable接口
package Thread;
/**
* @className: ThreadTest3
* @description:
* @createDate: 2021年06月25日 14:30:08
* @author: ns
*/
public class ThreadTest3 implements Runnable{
public static void main(String[] args) {
ThreadTest3 threadTest1 = new ThreadTest3();
//代理
new Thread(threadTest1).start();
for (int i = 0; i < 2000; i++) {
System.out.println("看代码" + i);
}
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("学习多线程代码" + i);
}
}
}
总结:避免单继承的局限性,推荐使用Runnable实现多线程,方便同一个对象被多个线程使用
实现Callable接口
package org.ck.thread.callable;
import org.ck.thread.ThreadTest2;
import org.ck.thread.WebDownLoader;
import java.util.concurrent.*;
/**
* @className: TestCallAble
* @description: 实现callable 接口
* @createDate: 2021年06月25日 17:39:45
* @author: ns
*/
public class TestCallAble implements Callable<Boolean> {
private String url;
private String name;
public TestCallAble(String url, String name) {
this.url = url;
this.name = name;
}
public static void main(String[] args) {
TestCallAble threadTest1 = new TestCallAble("https://img-blog.csdnimg.cn/2021061516385914.png#pic_center", "图1.png");
TestCallAble threadTest2 = new TestCallAble("https://img-blog.csdnimg.cn/20210615175631519.png#pic_center", "图2.png");
TestCallAble threadTest3 = new TestCallAble("https://img-blog.csdnimg.cn/20210616110215957.png#pic_center", "图3.png");
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> r1 = ser.submit(threadTest1);
Future<Boolean> r2 = ser.submit(threadTest2);
Future<Boolean> r3 = ser.submit(threadTest3);
try {
System.out.println(r1.get());
System.out.println(r2.get());
System.out.println(r3.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
ser.shutdown();
}
@Override
public Boolean call() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url, name);
System.out.println("下载了文件名为" + name);
return true;
}
}
总结:相比Runnable和Thread实现的线程,callable,使用了线程池,并让线程有了返回结果。
并发问题
模拟抢票
package Thread;
/**
* @className: ThreadTest4
* @description:
* @createDate: 2021年06月25日 14:42:41
* @author: ns
*/
public class ThreadTest4 implements Runnable {
private int ticketNums = 10;
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张");
}
}
public static void main(String[] args) {
ThreadTest4 ticket = new ThreadTest4();
new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛党").start();
}
}
运行结果
不应该有-1
静态代理
package org.ck.thread.proystatic;
/**
* @className: StaticProxy
* @description: 静态代理:代理类似面向切面编程,注解和这个很像,找到横切面
* 真实对象与代理角色都要实现同一个接口
* 代理对象可以帮助真实对象做琐事
* 真实对象只专注自己的事
* @createDate: 2021年06月28日 11:05:39
* @author: ns
*/
public class StaticProxy {
public static void main(String[] args) {
//线程实现
new Thread(()-> System.out.println("爱")).start();
//结婚代理
new WeddingCompany(new You()).HappyMarry();
/* //自己结婚
You you = new You();
//代理结婚
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();*/
}
}
interface Marry {
//久旱逢甘露
//他乡遇故知
//金榜题名时
void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry {
@Override
public void HappyMarry() {
System.out.println("结婚");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry {
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void after() {
System.out.println("收尾款");
}
private void before() {
System.out.println("布置现场");
}
}
静态代理总结:代理和代理对象通过实现同一个接口完成静态代理。属于AOP面向切面编程。
Lambda表达式
函数式接口 案例
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
测试类
package org.ck.thread.lambda;
/**
* @className: LambdaTest
* @description:
* @createDate: 2021年06月28日 11:39:20
* @author: ck
*/
public class LambdaTest {
//静态内部类
static class Like2 implements ILike {
@Override
public void lambda() {
System.out.println("i like lambda2");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
//4.局部内部类
class Like3 implements ILike {
@Override
public void lambda() {
System.out.println("i like lambda3");
}
}
like = new Like3();
like.lambda();
//5.匿名内部类 没有类的名称,必须借助接口或父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
//6.用lambda简化
//一个接口只包含一个抽象方法,那么他就是一个函数式接口
like = () ->{
System.out.println("i like lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface ILike {
void lambda();
}
//2.实现类
class Like implements ILike {
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
运行结果
测试类2
package org.ck.thread.lambda;
/**
* @className: LambadLoveTest
* @description:
* @createDate: 2021年07月03日 18:18:49
* @author: ck
*/
public class LambdaLoveTest {
public static void main(String[] args) {
/* Love love = new Love();
love.love(2);*/
int b = 20;
ILove iLove = (a) -> {
System.out.println("测试" + a);
};
iLove = a -> {
System.out.println("测试" + a);
};
//
iLove = a -> System.out.println("测试" + a);
iLove.love(b);
}
}
interface ILove {
void love(int a);
}
class Love implements ILove {
@Override
public void love(int a) {
System.out.println("love one" + a);
}
}
运行结果
线程状态
new Thread() 创建状态
start就绪状态
run运行状态
sleep,wait 阻塞
dead run自然结束就是死亡
package org.ck.thread.status;
/**
* @className: TestStop
* @description: 测试stop 线程正常停止,不建议死循环
* 建议使用标志位-->设置一个标志位
* 不要使用stop或者destroy等过时或JDK不建议的方法
* @createDate: 2021年07月07日 11:19:45
* @author: ck
*/
public class TestStop implements Runnable {
//1.设置标志
boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run...Thread" + i++);
}
}
//2.设置公开方法停止线程
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if (i== 900) {
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
运行结果
PS:run中的i与main方法中的i不同,所以不用管run的I走到了哪里
线程休眠(Thread.sleep())
黄牛抢票模拟
package org.ck.thread.status;
/**
* @className: TestSleep
* @description: 模拟网络延时
* @createDate: 2021年07月07日 11:33:05
* @author: ck
*/
public class TestSleep implements Runnable{
private int ticketNums = 10;
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
//模拟延时
try {
Thread.sleep( 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张");
}
}
public static void main(String[] args) {
TestSleep ticket = new TestSleep();
new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛党").start();
}
}
运行结果
sleep自带锁
倒计时模拟
package org.ck.thread.status;
/**
* @className: TestSleep
* @description: 模拟网络延时
* @createDate: 2021年07月07日 11:33:05
* @author: ck
*/
public class TestSleep implements Runnable{
private int ticketNums = 10;
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
//模拟延时
try {
Thread.sleep( 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张");
}
}
public static void main(String[] args) {
TestSleep ticket = new TestSleep();
new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛党").start();
}
}
运行结果
线程礼让(Thread.yield())
package org.ck.thread.status;
/**
* @className: TestYield
* @description: 线程礼让不一定成功
* @createDate: 2021年07月07日 15:08:59
* @author: ck
*/
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield, "a").start();
new Thread(myYield, "b").start();
}
}
class MyYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName() + "线程停止执行");
}
}
运行结果
线程强制执行(join)
package org.ck.thread.status;
/**
* @className: TestJoin
* @description: 测试join方法
* @createDate: 2021年07月07日 15:26:59
* @author: ck
*/
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程vip来了" + i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 500; i++) {
if (i == 10) {
thread.join();//插队
}
System.out.println("main" + i);
}
}
}
运行结果
线程观测状态(thread.getState())
NEW 创建状态
RUNNABLE
运行状态
BLOCKED
阻塞状态
WAITING
等待状态
TIMED_WAITING
等待零一个线程达到指定时间
TERMINATED
退出的线程
package org.ck.thread.status;
/**
* @className: TestState
* @description:
* @createDate: 2021年07月07日 15:54:22
* @author: ck
*/
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);
//观察启动后
thread.start();//启动
state = thread.getState();
System.out.println(state);//run
while (state != Thread.State.TERMINATED) {
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
运行结果
线程优先级(setPriority())
package org.ck.thread.status;
/**
* @className: TestPriority
* @description:
* @createDate: 2021年07月07日 16:02:41
* @author: ck
*/
public class TestPriority {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
//设置优先级启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(Thread.MAX_PRIORITY);
t3.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
运行结果
守护线程(setDaemon(true))
package org.ck.thread.status;
/**
* @className: TestDaemon
* @description: 测试守护线程
* @createDate: 2021年07月07日 16:11:27
* @author: ck
*/
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认false为用户线程
thread.start();//守护线程启动
new Thread(you).start();//
}
}
class God implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("上帝保佑着你");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一生都开心的活着");
}
System.out.println("=======hello,world");
}
}
运行结果
总结:虚拟机回确保主线程执行完毕,但不会确认守护线程一定能执行完成(守护线程常有,GC)
三个不安全例子
不安全的取钱
package org.ck.thread.syn;
/**
* @className: UnsafeBank
* @description: 不安全的取钱
* @createDate: 2021年07月08日 18:25:59
* @author: ck
*/
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"基金");
Drawing you = new Drawing(account,50,"你");
Drawing youFriend = new Drawing(account,100,"你朋友");
you.start();
youFriend.start();
}
}
class Account {
int money;//余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行
class Drawing extends Thread {
Account account;//账户
//取了多少钱
int drawingMoney;
//手里有多少钱
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
public void run() {
//判断有没有钱
if (account.money - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "钱不够");
return;
}
//sleep
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额 = 余额 - 你取的钱
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为" + account.money);
System.out.println(this.getName() + "手里的钱" + nowMoney);
}
}
运行结果
问题:基金余额不能-50
不安全的买票
package org.ck.thread.syn;
/**
* @className: UnsafeBuyTicket
* @description: 不安全的买票
*
* @createDate: 2021年07月08日 18:18:04
* @author: ck
*/
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "苦逼的我").start();
new Thread(buyTicket, "牛逼的你们").start();
new Thread(buyTicket, "可恶的黄牛党").start();
}
}
class BuyTicket implements Runnable {
private int ticketNums = 10;
boolean flag = true;//外部停止方式
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
if (ticketNums <= 0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
问题不应该有两次1
不安全的List
package org.ck.thread.syn;
import java.util.ArrayList;
import java.util.List;
/**
* @className: UnSafeList
* @description: 线程不安全的集合
* @createDate: 2021年07月08日 18:44:00
* @author: ck
*/
public class UnSafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
//两个线程操作了同一个位置,导致有数据被覆盖了
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
运行结果
问题:能明显观察到少了两个
多个线程往同一个位置插入了数据
线程安全
通过队列+锁来实现多线程的安全
安全的取钱
package org.ck.thread.syn;
/**
* @className: UnsafeBank
* @description: 不安全的取钱
* @createDate: 2021年07月08日 18:25:59
* @author: ck
*/
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100, "基金");
Drawing you = new Drawing(account, 50, "你");
Drawing youFriend = new Drawing(account, 100, "你朋友");
you.start();
youFriend.start();
}
}
class Account {
int money;//余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行
class Drawing extends Thread {
Account account;//账户
//取了多少钱
int drawingMoney;
//手里有多少钱
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
public void run() {
synchronized (account) {
//判断有没有钱
if (account.money - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "钱不够");
return;
}
//sleep
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额 = 余额 - 你取的钱
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为" + account.money);
System.out.println(this.getName() + "手里的钱" + nowMoney);
}
}
}
总结:是用户发起的取钱行为那就锁用户,让请求的用户排好队。
安全的买票
package org.ck.thread.syn;
/**
* @className: UnsafeBuyTicket
* @description: 不安全的买票
*
* @createDate: 2021年07月08日 18:18:04
* @author: ck
*/
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "苦逼的我").start();
new Thread(buyTicket, "牛逼的你们").start();
new Thread(buyTicket, "可恶的黄牛党").start();
}
}
class BuyTicket implements Runnable {
private int ticketNums = 10;
boolean flag = true;//外部停止方式
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized 同步方法 锁的是this
private synchronized void buy() throws InterruptedException {
//判断是否有票
if (ticketNums <= 0) {
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
安全的集合
package org.ck.thread.syn;
import java.util.ArrayList;
import java.util.List;
/**
* @className: UnSafeList
* @description: 线程不安全的集合
* @createDate: 2021年07月08日 18:44:00
* @author: ck
*/
public class UnSafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
//两个线程操作了同一个位置,导致有数据被覆盖了
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁
package org.ck.thread.syn;
/**
* @className: DeadLock
* @description: 死锁
* @createDate: 2021年07月15日 10:03:12
* @author: ck
*/
public class DeadLock {
public static void main(String[] args) {
Makeup girl = new Makeup(0,"灰姑凉");
Makeup girl1 = new Makeup(1,"白雪公主");
girl.start();
girl1.start();
}
}
class Lipstick {
}
class Mirror {
}
class Makeup extends Thread {
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;
public Makeup(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
//化妆
}
//化妆 互相持有对方的锁
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
Thread.sleep(1000);
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
}
}
}else{
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
Thread.sleep(2000);
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
}
}
}
}
}
灰姑凉想获得镜子
白雪公主想要获得口红
但是镜子在白雪公主手上
口红在灰姑凉手上
就造成了死锁
解决方案:白雪公主和灰姑凉用完各自的东西,将其放回去就不会死锁了。
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
}
}else{
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
}
}
}
运行结果
LOCK锁
package org.ck.thread.lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @className: TestLock
* @description:
* @createDate: 2021年07月16日 18:12:23
* @author: ck
*/
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable {
int ticketNums = 10;
//定义lock锁
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (ticketNums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
} else {
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
运行结果
synchronized和Lock的差异
synchronized
java 关键字
会自动释放锁
lock
只是一个类
需要手动释放锁
lock相比synchronize的可操作性更强
总结
package org.ck.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @className: ThreadNew
* @description: 回顾线程的创建
* @createDate: 2021年07月19日 11:26:59
* @author: ck
*/
public class ThreadNew {
public static void main(String[] args) {
new MyThread1().start();
new Thread(new MyThread2()).start();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
Integer integer = null;
try {
integer = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(integer);
}
}
//1.继承Thread类
class MyThread1 extends Thread{
public void run(){
System.out.println("MyThread1");
}
}
//2.实现Runnable接口
class MyThread2 implements Runnable{
public void run(){
System.out.println("MyThread2");
}
}
//实现CallAble
class MyThread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("MyThread3");
return 100;
}
}
以上内容学自B站狂神JAVA
https://www.bilibili.com/video/BV1V4411p7EF?from=search&seid=13950488010332720509