参考于遇见狂神说视频以及学习笔记
核心概念
线程创建的方式(重要)
//方式一:继承Thread类,重写run()方法,调用start开启线程
// 线程开启不一定立即执行,由cpu调度执行
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/14-15:41
*/
//方式一:继承Thread类,重写run()方法,调用start开启线程
// 线程开启不一定立即执行,由cpu调度执行
public class TestThread1 extends Thread{
@Override
public void run() {
//线程体
for (int i = 200; i > 0; i--) {
System.out.println("我在学习多线程"+i);
}
}
public static void main(String[] args) {
//主线程
//创建一个线程对象
TestThread1 testThread1 =new TestThread1();
//调用start方法开启线程
testThread1.start();
for (int i = 1000; i > 0; i--) {
System.out.println("我在看代码"+i);
}
}
}
实现runnable接口,重写run方法,创建线程对象,执行线程丢入runnable接口实现类,调用start方法
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/14-15:58
*/
//方式2:实现runnable接口,重写run方法,创建线程对象,执行线程丢入runnable接口实现类,调用start方法
public class TestThread2 implements Runnable {
@Override
public void run() {
//线程体
for (int i = 200; i > 0; i--) {
System.out.println("我在学习多线程"+i);
}
}
public static void main(String[] args) {
//主线程
TestThread2 testThread2 =new TestThread2();
new Thread(testThread2).start();
for (int i = 1000; i > 0; i--) {
System.out.println("我在看代码"+i);
}
}
}
两种方式总结
实现callable接口,开启多线程
package com.Thread;
import java.util.concurrent.*;
/**
* @Description
* @autor wzl
* @date 2022/8/14-16:28
*/
public class TestThread4 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+" i="+i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestThread4 testThread1 =new TestThread4();
TestThread4 testThread2 =new TestThread4();
TestThread4 testThread3 =new TestThread4();
//创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行:
Future<Boolean> result1 = ser.submit(testThread1);
Future<Boolean> result2 = ser.submit(testThread2);
Future<Boolean> result3 = ser.submit(testThread3);
//获取结果:
boolean r1 = result1.get();
boolean r2 = result2.get();
boolean r3 = result3.get();
//关闭服务:
ser.shutdownNow();
}
}
使用runnable接口用到了静态代理的知识,
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色 使用静态代理的好处:
- 代理对象可以做很多真实对象无法做的事情
- 真实对象专注于做自己的事情
package com.Thread;
import javax.activation.MailcapCommandMap;
import java.util.Map;
/**
* @Description
* @autor wzl
* @date 2022/8/14-16:45
*/
//静态代理模式
public class staticTest {
public static void main(String[] args) {
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("结婚前,布置现场");
}
}
线程的5大状态
常用方法
如何停止线程
1.建议线程正常停止—》利用次数,不建议死循环
2.建议使用标志位—》设置一个标志位
3.不要使用stop或destroy等过时或jdk不建议使用的方法
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-8:33
* 测试stop
1.建议线程正常停止---》利用次数,不建议死循环
2.建议使用标志位---》设置一个标志位
3.不要使用stop或destroy等过时或jdk不建议使用的方法
*
*/
public class Thread5 implements Runnable {
//1.设置一个标志位
private 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) {
Thread5 thread5=new Thread5();
new Thread(thread5).start();
//调用stop方法切换标志位,让线程停止
for (int i = 1; i <=1000; i++) {
System.out.println("main..."+i);
if (i == 900) {
thread5.stop();
System.out.println("线程停止了。。。");
}
}
}
}
线程休眠
- sleep()方法的用处:
- 1.模拟网络延迟,放大线程中的并发问题
- 2.模拟倒计时
- 3.获取当前系统时间
- 模拟倒计时
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-8:50
*
* sleep()方法的用处:
* 1.模拟网络延迟,放大线程中的并发问题
* 2.模拟倒计时
* 3.获取当前系统时间
*/
public class TestSleep {
public static void main(String[] args) {
try {
testDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void testDown() throws InterruptedException {
int num=10;
while (true) {
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
}
- 打印当前系统时间
package com.Thread;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.SimpleFormatter;
/**
* @Description
* @autor wzl
* @date 2022/8/16-8:50
*
* sleep()方法的用处:
* 1.模拟网络延迟,放大线程中的并发问题
* 2.模拟倒计时
* 3.获取当前系统时间
*/
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
//打印当前系统时间
Date date=new Date(System.currentTimeMillis());//获取系统当前时间
while (true) {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date=new Date(System.currentTimeMillis());//更新系统时间
}
}
}
线程礼让
礼让不一定能成功,最终决定权在cpu如何调度
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-9:10
*
* 测试礼让,礼让不一定能成功,最终决定权在cpu如何调度
*/
public class TestYield {
public static void main(String[] args) {
MyYile myYile=new MyYile();
new Thread(myYile,"a").start();
new Thread(myYile,"b").start();
}
}
class MyYile implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程执行结束");
}
}
Join介绍
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-9:25
* //join方法,相当于插队
*/
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i <=500; 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 < 200; i++) {
if(i==100)
thread.join(); //vip会员插队
System.out.println("main开始执行"+i);
}
}
}
线程状态的观测
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-9:37
*/
public class TestThreadState {
public static void main(String[] args) throws InterruptedException {
Thread thread =new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} 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);
//
while (state!=Thread.State.TERMINATED){
//只要线程不停止,就一直输出状态
Thread.sleep(100);
state=thread.getState(); //更新线程状态
System.out.println(state);
}
}
}
线程优先级
注意:优先级高的不一定先执行,只不过是执行的概率较大
守护线程
默认是false,代表用户线程(main线程),守护线程(Gc线程)
线程同步(重要)
什么是并发?
什么是线程同步?
处理多线程问题时 , 多个线程访问同一个对象 , 并且某些线程还想修改这个对象 . 这时候我们就需要线程同步。线程同步其实就是一种等待机制 , 多个需要同时访问此对象的线程进入这个对象的等待池形成队列, 等待前面线程使用完毕 , 下一个线程再使用
采用队列+锁的方式解决
线程不安全举例
- 不安全的买票(会出现负数)
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-11:03
* <p>
* 模拟不安全的买票
* 线程不安全,票数有负数
*/
public class TestUnsaveBuyTicket {
public static void main(String[] args) {
buyTicket buy = new buyTicket();
new Thread(buy, "wzl").start();
new Thread(buy, "zzz").start();
new Thread(buy, "www").start();
}
}
class buyTicket implements Runnable {
private int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
try {
while (flag) {
buy();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//买票
private void buy() throws InterruptedException {
while (ticketNums <= 0) {
flag = false;
return;
}
Thread.sleep(10);
//买票
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
- 不安全的取票
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-11:16
* 不安全取钱
* 两个人同时去银行取钱
*/
public class TestMoney {
public static void main(String[] args) {
Account account=new Account(100,"结婚金钱");
Bank you =new Bank(account,50,"你");
Bank girl =new Bank(account,100,"女朋友");
you.start();
girl.start();
}
}
//账户
class Account {
int money; //余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行,模拟取款
class Bank extends Thread {
Account account; //账户
int drawmoney; //取的钱
int nowmoney; //手里的钱
public Bank(Account account, int drawmoney,String name) {
super(name);
this.account = account;
this.drawmoney = drawmoney;
}
//用户取钱
@Override
public void run() {
//钱不够
if (account.money - drawmoney < 0) {
System.out.println(Thread.currentThread().getName() + "钱不够,不能取");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额
account.money = account.money - drawmoney;
//手里的钱
nowmoney = nowmoney + drawmoney;
System.out.println(account.name + "余额为:" + account.money);
System.out.println(this.getName() + "手里的钱" + nowmoney);
}
}
- 线程不安全的集合
由于ArrayList是线程不安全的,在测试添加10000条数据后,由于不安全会出现添加的数据不够10000条数据,因为会出现同时抢占同一块内存空间,然后会覆盖原先的值,因此不够10000条大小
package com.Thread;
import java.util.ArrayList;
import java.util.List;
/**
* @Description
* @autor wzl
* @date 2022/8/16-11:52
*/
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());
}
}
如何解决线程不安全问题
使用synchronized 关键词,构建synchronized 方法 和synchronized 块 .
需要修改内容的时候才使用synchronized同步方法,否则会浪费资源
- 修改后的安全取票
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-11:03
* <p>
* 模拟不安全的买票
* 线程不安全,票数有负数
*/
public class TestUnsaveBuyTicket {
public static void main(String[] args) {
buyTicket buy = new buyTicket();
new Thread(buy, "wzl").start();
new Thread(buy, "zzz").start();
new Thread(buy, "www").start();
}
}
class buyTicket implements Runnable {
private int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
try {
while (flag) {
buy();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//买票,变为同步方法
private synchronized void buy() throws InterruptedException {
while (ticketNums <= 0) {
flag = false;
return;
}
Thread.sleep(10);
//买票
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
- 修改后的银行取款
注意:这里不能用synchronized直接修饰run()方法,由于变化的量是accout用户的余额,且两个人共享的资源是同一个账户,而不是共享的银行,因此需要使用同步块进行处理。
同步块中的变量应该进行是增、删、改的变量
package com.Thread;
/**
* @Description
* @autor wzl
* @date 2022/8/16-11:16
* 不安全取钱
* 两个人同时去银行取钱
*/
public class TestMoney {
public static void main(String[] args) {
Account account=new Account(100,"结婚金钱");
Bank you =new Bank(account,50,"你");
Bank girl =new Bank(account,100,"女朋友");
you.start();
girl.start();
}
}
//账户
class Account {
int money; //余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行,模拟取款
class Bank extends Thread {
Account account; //账户
int drawmoney; //取的钱
int nowmoney; //手里的钱
public Bank(Account account, int drawmoney,String name) {
super(name);
this.account = account;
this.drawmoney = drawmoney;
}
//用户取钱
@Override
public void run() {
synchronized (account){
//钱不够
if (account.money - drawmoney < 0) {
System.out.println(Thread.currentThread().getName() + "钱不够,不能取");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额
account.money = account.money - drawmoney;
//手里的钱
nowmoney = nowmoney + drawmoney;
System.out.println(account.name + "余额为:" + account.money);
System.out.println(this.getName() + "手里的钱" + nowmoney);
}
}
}
- 修改后的安全集合
package com.Thread;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
* @Description
* @autor wzl
* @date 2022/8/16-11:52
*/
public class unsafeList {
public static void main(String[] args) {
List<String> list =new ArrayList<>();
for (int i = 0; i < 30000; 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 com.Thread;
import java.util.List;
/**
* @Description
* @autor wzl
* @date 2022/8/16-15:24
*/
public class TestSiSuo {
public static void main(String[] args) {
Makeup girl1=new Makeup(0,"小红");
Makeup girl2=new Makeup(1,"小花");
girl1.start();
girl2.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
//需要的资源只有一份,用static保证
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;//选择
String girlName;//女孩名字
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){
//1秒后想获得镜子
System.out.println(this.girlName+"获得了镜子的锁");
}
}
}else {
synchronized (mirror){
//持有镜子的锁
System.out.println(this.girlName+"获得了镜子的锁");
Thread.sleep(1000);
synchronized (lipstick){
//1秒后想获得口红
System.out.println(this.girlName+"获得了口红的锁");
}
}
}
}
}
- 解决死锁的代码
package com.Thread;
import java.util.List;
/**
* @Description
* @autor wzl
* @date 2022/8/16-15:24
*/
public class TestSiSuo {
public static void main(String[] args) {
Makeup girl1=new Makeup(0,"小红");
Makeup girl2=new Makeup(1,"小花");
girl1.start();
girl2.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
//需要的资源只有一份,用static保证
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;//选择
String girlName;//女孩名字
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){
//1秒后想获得镜子
System.out.println(this.girlName+"获得了镜子的锁");
}
}else {
synchronized (mirror){
//持有镜子的锁
System.out.println(this.girlName+"获得了镜子的锁");
Thread.sleep(1000);
}
synchronized (lipstick){
//1秒后想获得口红
System.out.println(this.girlName+"获得了口红的锁");
}
}
}
}
死锁避免的方法
Lock锁
可重入锁
- 使用形式
- 举例代码
package com.Thread;
import java.beans.beancontext.BeanContextServiceRevokedListener;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Description
* @autor wzl
* @date 2022/8/16-15:49
*/
public class TestLock {
public static void main(String[] args) {
Ticket ticket =new Ticket();
new Thread(ticket).start();
new Thread(ticket).start();
new Thread(ticket).start();
}
}
class Ticket implements Runnable{
int ticketNum=10;
//定义lock锁
private final ReentrantLock lock =new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock(); //加锁
if(ticketNum>0) {
System.out.println(ticketNum--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}finally {
lock.unlock(); //解锁
}
}
}
}
synchronized vs lock
package com.Thread;
import jdk.internal.dynalink.ChainedCallSite;
/**
* @Description
* @autor wzl
* @date 2022/8/16-16:07
* <p>
* 测试:生产者消费者模型--》利用缓冲区解决:管程法
* 生产者,消费者,产品,缓冲区
*/
public class TestPc {
public static void main(String[] args) {
SynContainer synContainer= new SynContainer();
new productor(synContainer).start();
new consumer(synContainer).start();
}
}
class productor extends Thread {
SynContainer synContainer;
public productor(SynContainer synContainer){
this.synContainer=synContainer;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synContainer.push(new chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
class consumer extends Thread{
SynContainer synContainer;
public consumer(SynContainer synContainer){
this.synContainer=synContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->"+synContainer.pop().id+"只鸡");
}
}
}
class chicken {
int id; //产品标号
public chicken(int id) {
this.id = id;
}
}
class SynContainer {
//定义一个容器,存放产品
chicken[] chickens = new chicken[50];
//定义计数器
int count = 0;
//生产者放入产品
public synchronized void push(chicken chicken) {
//如果容器满了,就等待消费者消费
if(count==chickens.length){
//通知消费者,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就放入产品
chickens[count]=chicken;
count++;
//可以通知消费者消费了
this.notify();
}
//消费者消费产品
public synchronized chicken pop(){
//判断能否消费
if(count==0){
//通知生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有产品,进行消费
count--;
chicken chicken=chickens[count];
//吃完后,通知生产者生产
this.notify();
return chicken;
}
}