1 线程简介
1.1多任务
看起来是多个任务一起做,但实际上我们的大脑在同一时间,依旧只是在做一件事
1.2多线程
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
1.3普通方法调用 和 多线程的区别
1.4进程和线程
一个进程可以包括多个线程 最直观地理解 就是在看视频的时候 可以同时 看到视频 听到声音 看到弹幕等等 注意:很多多线程是模拟出来的,真正的多线程指的是拥有多个cpu,即多核。模拟出来的多线程 通常情况下是cpu切换的很快 造成了多线程的错觉 。对同一份资源进行操作时,会存在资源抢夺的问题,需要加入并发控制。
2 线程创建(重要)
2.1三种创建方式
2.2Thread
第一步:创建线程类继承Thread类
第二步:重写run()方法 编写线程执行体
第三步:创建线程对象 执行start()方法 启动线程
上代码:
package com.kuang.demo1;
//创建线程方式一:继承Thread类,重写run()方法,调用start()方法
public class TestThread1 extends Thread{
@Override
public void run(){
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
//主线程
//创建一个线程对象
TestThread1 testThread1 = new TestThread1();
//开启线程
testThread1.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习主线程--"+i);
}
}
}
结果:
注意,线程开启并不一定立即执行,由cpu调度执行
2.3Runnable
第一步:定义MyRunnable类实现Runnable接口
第二步:实现run方法,编写线程执行体
第三步:创建线程对象 调用start()启动线程
代码:
package com.kuang.demo1;
//创建线程方式二:实现Runnable接口 重写run方法 执行线程需要丢入runnable接口实现类 调用start方法
public class TestThread2 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread2 testThread2 = new TestThread2();
//创建线程对象,通过线程对象来开启线程(代理)
Thread thread1 = new Thread(testThread2);
thread1.start();
//前三行 等价于 下面 一行
new Thread(testThread2).start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习主线程--"+i);
}
}
}
结果:
比较:
3 初识并发问题
3.1购买火车票例子
package com.kuang.demo1;
//并发问题 火车票购买例子
public class TestThread3 implements Runnable{
//票数
private int ticketNums = 10;
@Override
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) {
TestThread3 ticket = new TestThread3();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
}
}
运行后我们发现
相信我们已经发现了并发问题的所在
当多个线程操作同一资源的情况下,线程不安全,数据紊乱。如何解决?在下文会提到
4 案例龟兔赛跑
package com.kuang.demo1;
//模拟龟兔赛跑
public class Race implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
}
}
//判定是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if (winner!=null){
return true;
}else {
if (steps>=100){
winner=Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
5 Callable
package com.kuang.demo1;
import java.util.concurrent.*;
public class TestCallable implements Callable {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("我在看代码---"+i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable testCallable = new TestCallable();
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行
Future result1 = ser.submit(testCallable);
//获得结果
boolean r1 =result1.get();
//关闭服务
ser.shutdownNow();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习主线程--"+i);
}
}
}
Callable可以定义返回值,抛出异常 相比Runnable 更加安全 工作常用。
6 静态代理
package com.kuang.demo1;
//静态代理模式总结:
//真实对象和代理对象都要实现同一接口
//代理对象要代理真实角色,他还可以做些额外的事情
public class StacticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You("小明"));
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实角色
class You implements Marry{
private String name;
You(){
}
You(String name){
this.name=name;
}
@Override
public void HappyMarry() {
System.out.println(name+"结婚了,超开心!");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
//代理谁-》真实目标角色
private Marry target;
WeddingCompany(Marry target){
this.target=target;
}
@Override
public void HappyMarry() {
before();
target.HappyMarry();//这就是真实对象
after();
}
private void after() {
System.out.println("结婚后,收尾款");
}
private void before() {
System.out.println("结婚前,布置现场");
}
}
7 线程状态(重要)
7.1线程停止
package com.kuang.demo1;
//1.建议线程正常停止-> 利用次数,不建议死循环
//2. 建议使用标志位-> 设置一个标志位
//3. 不要使用jdk弃用的方法
public class TestStop implements Runnable{
//设置标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("run...Thread"+i++);
}
}
//转换标志位
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("线程该停止了");
}
}
}
}
7.2线程休眠
package com.kuang.demo1;
//模拟网络延时:放大问题的发生性
public class TestSleep implements Runnable{
private int ticketNums = 10;
@Override
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) {
TestThread3 ticket = new TestThread3();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
}
}
如此让我们意识到线程并发问题
7.3线程礼让
package com.kuang.demo1;
//礼让不一定成功 看cpu心情
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()+"线程停止");
}
}
7.4线程强制执行
package com.kuang.demo1;
//测试Join 想象成插队
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==200){
thread.join();//插队
}
System.out.println("main"+i);
}
}
}
7.5观察线程状态
package com.kuang.demo1;
public class TestState {
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);
}
}
}
7.6线程优先级
首先知道一点 从1-10 等级越高 cpu分配的权重越大 越容易被分派资源 但不绝对
package com.kuang.demo1;
//测试线程的优先级
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);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);
//先设置优先级 再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//10
t4.start();
t5.setPriority(-1);
t5.start();
t6.setPriority(11);
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
等级是从1到10 否则会报错的。
7.7守护线程
package com.kuang.demo1;
//测试守护线程
//上帝守护你
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
you you1 = new you();
Thread thread = new Thread(god);
thread.setDaemon(true);
thread.start();//上帝守护线程启动
new Thread(you1).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("-====goodbye! world!");//hello world;
}
}
理论来说上帝线程不应该结束 但由于我们设置为守护线程 要守护的线程结束后 该线程跑了一会 也结束了。
8 线程同步(重点 难点)
8.1 线程同步机制
8.2三大不安全案例
不安全的火车站买票实例
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--+"张票");
}
}
不安全的银行取钱实例
package com.kuang.demo1;
//不安全的取钱
//两人去取钱 账户
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(100,"结婚基金");
Drawing you = new Drawing(account,50,"你");
Drawing yourFriend = new Drawing(account,100,"yourFriend");
you.start();
yourFriend.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(100);
} 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);
}
}
一共存了100 现在共有150 明显发生了Bug
不安全的集合实例
package com.kuang.demo1;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) {
List list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
这里我们应该得到数字1000 但运行后结果是
8.3同步方法及同步块
安全的买票实例
package com.kuang.demo1;
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();
//模拟延时
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized 加了该关键字后 方法就变成了同步方法 实现了锁
private synchronized void buy() {
//判断是否由票
if (ticketNums<=0){
flag=false;
return;
}
System.out.println(Thread.currentThread().getName()+"拿到第"+ticketNums--+"张票");
}
}
只需要加上关键字 synchronized 再改变sleep()的位置即可
程序从 各个线程之间“抢票” 变为了 排队购票 就实现了线程的安全
安全的银行取钱实例
package com.kuang.demo1;
//不安全的取钱
//两人去取钱 账户
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(100,"结婚基金");
Drawing you = new Drawing(account,50,"你");
Drawing yourFriend = new Drawing(account,50,"yourFriend");
you.start();
yourFriend.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(100);
} 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 com.kuang.demo1;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(300);
System.out.println(list.size());
}
}
8.4CopyOnWriteArrayList
这是一个线程安全的集合类 (了解)
package com.kuang.demo1;
import java.util.concurrent.CopyOnWriteArrayList;
//测试Juc安全类型的集合
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList list = new CopyOnWriteArrayList();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(300);
System.out.println(list.size());
}
}
8.5死锁
实例我们还用买票系统
package com.kuang.demo1;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2,"黄牛1").start();
new Thread(testLock2,"黄牛2").start();
new Thread(testLock2,"黄牛3").start();
}
}
class TestLock2 implements Runnable{
int tickNums = 100;
//定义Lock锁
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try {
lock.lock();//加锁
if (tickNums > 0) {
System.out.println(Thread.currentThread().getName()+"拿到了第"+tickNums--+"张票");
} else {
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
9 线程协作
9.1生产者消费者问题
true 就等待 false 就去唤醒
9.2信号灯法
package com.kuang.demo1;
public class TestPc2 {
public static void main(String[] args) {
Tv tv = new Tv();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者->演员
class Player extends Thread{
Tv tv;
public Player(Tv tv){
this.tv=tv;
}
@Override
public void run(){
for (int i = 0; i < 20; i++) {
if (i%2==0){
try {
this.tv.play("快乐大本营播放着");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
try {
this.tv.play("广告");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//消费者->观众
class Watcher extends Thread{
Tv tv;
public Watcher(Tv tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
tv.watch();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//产品->节目
class Tv{
String voice;
boolean flag = true;
//表演
public synchronized void play(String voice) throws InterruptedException {
if (!flag){
this.wait();
}
System.out.println("演员表演了"+voice);
//通知观众观看
this.notifyAll();//通知唤醒
this.voice=voice;
this.flag=!this.flag;
}
public synchronized void watch() throws InterruptedException {
if (flag){
this.wait();
}
System.out.println("观看了"+voice);
//通知演员表演
this.notifyAll();
this.flag=!this.flag;
}
}
可以理解为 演员表演了什么 观众就观看什么 且顺序符合逻辑
9.3线程池
package com.kuang.demo1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭链接
service.shutdownNow();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
================================我是分割线 ================================
本篇为学习狂神讲的多线程的笔记 屏幕前的你如果想跟着视频再敲一遍 地址如下:
【狂神说Java】多线程详解_哔哩哔哩_bilibili
不要忘记三连哦!(●'◡'●)