多线程
核心概念
-
线程就是独立的执行路径,而进程是一个动态的概念,进程相当于是多个线程的保护伞
-
在程序运行时,及时没有自己创建的线程,后台也会有多个线程,如主线程,gc线程
-
main()称之为主线程,系统的入口,用于执行整个程序
-
多线程之间的顺序是不能人为干预的
-
对于同一份资源的操作,可能会有争抢,所以要加入并发控制(让所有人排队)
-
线程会带来额外的开销(cpu调度时间,并发控制开销)
-
线程中的数据一定要同步
创建线程
方式一
-
继承Thread类 (不怎么使用:避免oop单继承局限性)
-
重写run()方法,编写线程体
-
创建一个线程对象
-
调用start开启线程
public class TestThread1 extends Thread { //1
@Override //2
public void run() {
//run 线程体
for (int i = 0; i < 200; i++) {
System.out.println("执行Run线程"+i);
}
}
public static void main(String[] args) {
//main 线程体
TestThread1 testThread1 = new TestThread1(); //3
testThread1.start(); //4
for (int i = 0; i < 200; i++) {
System.out.println("执行Main线程"+i);
}
}
}
//两个方法交替执行
注意:线程开启不一定会立即执行,会由CPU来调度
方式二
-
implements 实现Runnable接口 (用得多:灵活方便,方便同一个对象被多个线程使用)
-
实现run()方法,编写线程执行体
-
先创建实现类对象
-
再创建线程对象(把实现类对象放进去),调用start()方法启动线程
public class TestThread2 implements Runnable { //1
@Override
public void run() { //2
//run线程
for (int i = 0; i < 200; i++) {
System.out.println("执行run线程"+i);
}
}
public static void main(String[] args) {
//main线程
TestThread2 testThread2 = new TestThread2(); //3
Thread thread = new Thread(testThread2); //4
thread.start();
for (int i = 0; i < 200; i++) {
System.out.println("执行main线程"+i);
}
}
}
多个线程操作同一个对象
买火车票
public class TestThread3 implements Runnable {
private int tikect = 10;
@Override
public void run() {
while (true) {
if (tikect<=0) {
break;
}
System.out.println(Thread.currentThread().getName()+"拿到第"+tikect--+ "张票");
}
}
public static void main(String[] args) {
TestThread3 tk = new TestThread3();
//多个线程操作一个对象
Thread t1 = new Thread(tk,"小明");
Thread t2 = new Thread(tk,"小花");
Thread t3 = new Thread(tk,"小郭");
t1.start();
t2.start();
t3.start();
}
}
发现问题:多个线程操作同一个资源的时候,线程不安全,数据紊乱
解决:队列 + 🔒
静态代理
好处:代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
public class StaticProxy {
public static void main(String[] args) {
// You you = new You();
// you.happyMarry();
Wedding wedding = new Wedding(new You());
wedding.happyMarry();
}
}
interface Marry{
void happyMarry();
}
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("结婚啦");
}
}
class Wedding implements Marry{
private Marry target;
public Wedding(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("结婚前的准备");
}
}
总结:真实对象和代理对象都要实现同一个接口
代理对象要代理真是角色 (构造方法传入)
Thread 实现 Runnable是一个道理~
Lambda 表达式
最大的大前提:接口为函数式接口,一个接口里面只写了一个函数
目的:
-
避免匿名内部类定义过多
-
可以让代码看起来更简洁
-
去掉很多没有意义的代码
演变过程:外部实现类-->静态内部类-->局部内部类-->匿名内部类(没有类名,并且方法名字都是一样的所以直接去掉)-->Lambda
public class TestLambda1 {
//3.静态内部类
static class Love1 implements Ilove{
@Override
public void iLove(int a) {
System.out.println("I Love YOU--->"+a);
}
}
public static void main(String[] args) {
//4.局部内部类
class Love2 implements Ilove{
@Override
public void iLove(int a) {
System.out.println("I Love YOU--->"+a);
}
}
Ilove love = new Love();
love.iLove(2);
love = new Love1();
love.iLove(3);
love = new Love2();
love.iLove(4);
//5.匿名内部类
Ilove ilove = new Ilove(){
@Override
public void iLove(int a) {
System.out.println("I Love YOU--->"+a);
}
};
ilove.iLove(5);
}
}
//1.接口类
interface Ilove{
void iLove(int a);
}
//2.外部实现类
class Love implements Ilove{
@Override
public void iLove(int a) {
System.out.println("I Love YOU--->"+a);
}
}
Lambda简化:
//6.Lambda简化
ilove = (int a)-> {
System.out.println("I Love YOU--->"+a);
};
ilove.iLove(6);
Lambda简化终极爆炸版:
前提:只能有一行表达式,多行还是需要用花括号包括起来
多个参数也可以去掉参数类型,要去掉都去掉,必须加上括号,用逗号隔开
ilove = a->System.out.println("I Love YOU--->"+a);
ilove.iLove(6);
停止线程
推荐线程自己停下来:
怎么做呢:使用一个标志位进行终止变量,当flag=false,则终止。
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 static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 200; i++) {
System.out.println("main........thread"+i);
if (i==150) {
testStop.stop();
System.out.println("该线程停止了");
}
}
}
public void stop(){
this.flag = false;
}
}
线程休眠
-
sleep指定当前线程阻塞的毫秒数:1000->1s
-
sleep存在异常InterruptedException
-
sleep时间打倒后线程进入就绪状态
-
sleep可以模拟网络延时,倒计时等
-
每一个对象都有一个🔒,sleep不会释放锁
模拟倒计时
public class TestSleep implements Runnable {
@Override
public void run() {
int num = 10;
while (true) {
try {
Thread.sleep(1000);
System.out.println("倒计时"+num--);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (num<=0) {
break;
}
}
}
public static void main(String[] args) {
TestSleep testSleep = new TestSleep();
new Thread(testSleep).start();
}
}
模拟时钟
public class TestSleep implements Runnable {
@Override
public void run() {
Date startTime = new Date(System.currentTimeMillis());
while (true) {
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TestSleep testSleep = new TestSleep();
new Thread(testSleep).start();
}
}
线程礼让
礼让不一定成功,礼让后线程之间重新竞争,CPU还是老大哥
Thread.yield
线程强制执行(插队)
天龙人地位高,我走完了你才能走.....
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);
}
}
}
守护线程
God bless you my man~!
-
线程分为用户线程和守护线程
-
虚拟机必须确保用户线程执行完毕
-
不用等守护线程执行完毕
GC线程 记录操作日志 监控内存都是守护线程
public class TestDaemon {
public static void main(String[] args) {
We we = new We();
Thread thread = new Thread(we);
God god = new God();
Thread thread1 = new Thread(god);
thread1.setDaemon(true); //默认false为用户线程 , true为守护线程
thread.start();
thread1.start();
}
}
class We implements Runnable{
@Override
public void run() {
for (int i = 0; i < 365; i++) {
System.out.println("我还活着!!"+i);
}
System.out.println("上票");
}
}
class God implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("God bless you my man~");
}
}
}
synchronized
-
在方法上修饰的时候可以给这个方法上锁,不同线程访问这个方法就要看是否有锁
-
用同步块:synchronized(Obj){}来🔒的话 关键在于这个Obj是什么
-
Obj是需要增删改的对象
同步方法,同步代码块
public class UnSafeBuyTicket implements Runnable{
private boolean flag = true;
private int ticket = 10;
@Override
public void run() {
//买票
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buy() throws InterruptedException {
//判断是否有票
if (ticket<=0) {
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"买到第"+ticket--+"张票");
}
public static void main(String[] args) {
UnSafeBuyTicket unSafeBuyTicket = new UnSafeBuyTicket();
new Thread(unSafeBuyTicket,"小明").start();
new Thread(unSafeBuyTicket,"小花").start();
}
}
死锁
产生的四个必要条件:
-
互斥条件:一个资源每次只能被一个进程使用
-
请求与保持条件:一个进程因请求资源而阻塞时,队以获得的资源保持不放
-
不剥夺条件:进程以获得的资源,在没有使用完的时候,不能被强制剥夺
-
循环等待条件:进程之间首尾相接循环等待资源
public class DeadLock {
public static void main(String[] args) {
MakeUp m1 = new MakeUp("小花", 0);
MakeUp m2 = new MakeUp("小红", 2);
m1.start();
m2.start();
}
}
//口红
class Lipstick {
}
//镜子
class Mirror {
}
class MakeUp extends Thread {
String girlName;
int choice;
private static Lipstick lipstick = new Lipstick();
private static Mirror mirror = new Mirror();
public MakeUp(String girlName, int choice) {
this.girlName = girlName;
this.choice = choice;
}
@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+"拿到口红");
}
}
}
}
}
解决:用完了释放🔒
public class DeadLock {
public static void main(String[] args) {
MakeUp m1 = new MakeUp("小花", 0);
MakeUp m2 = new MakeUp("小红", 2);
m1.start();
m2.start();
}
}
//口红
class Lipstick {
}
//镜子
class Mirror {
}
class MakeUp extends Thread {
String girlName;
int choice;
private static Lipstick lipstick = new Lipstick();
private static Mirror mirror = new Mirror();
public MakeUp(String girlName, int choice) {
this.girlName = girlName;
this.choice = choice;
}
@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+"拿到口红");
}
}
}
}
为了更好的控制多个线程对共享资源进行访问--->产生了一个工具类!它就是Lock!!当当当~~
java.util.concurrent.locks.Lock //接口
ReentrantLock 类实现了Lock -->更加常用 可重入锁
因为加锁之后最后都要释放锁
所以一般都与try finally一起用
快捷键: crtl +alt +t
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{
private int ticket = 10;
//定义Lock
private final ReentrantLock lock = new ReentrantLock(); //这一句很重要
@Override
public void run() {
while (true) {
try {
lock.lock(); //显示的加锁
if (ticket>0) {
System.out.println(ticket--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock(); //显示的释放锁
}
}
}
}
synchronized 与Lock 的对比
-
Lock时显示🔒 (手动挡)synchronized是隐式🔒,除了作用域自动释放(自动挡)
-
synchronized 比 Lock 多了一个方法🔒
-
使用Lock🔒,JVM花费时间少些,性能更好,并且具有更好的发展性(提高更多的子类)
-
优先使用顺序:
-
Lock > 同步代码块 > 同步方法
-
线程通信
wait(): 表示线程一直等待,直到其他线程通知,与Sleep不同,wait会释放锁 notify(): 唤醒处于等待中的线程 notifyAll(): 唤醒同一对象上所有调用wait()方法的线程,优先级别高的的线程优先调度
注意:均是Object类的方法,都只能再同步方法或者同步代码块中使用,否则会抛出异常
illegalMonitorStateException
生产者与消费者
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Productor productor = new Productor(container);
Consumer consumer = new Consumer(container);
productor.start();
consumer.start();
}
}
//生产者
class Productor extends Thread {
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产了" + i + "只鸡");
container.push(new Chicken(i));
}
}
}
//消费者
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
//消费
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+container.pop().id+"只鸡");
}
}
}
//缓冲区
class SynContainer {
//放鸡的容器
Chicken[] chickens = new Chicken[10];
//记录容器里面鸡的数量
int count = 0;
//生产者生产鸡
public synchronized void push(Chicken chicken){
//容器满了
if (count==chickens.length) {
//通知消费者消费
}
//没满就往容器里面放
chickens[count] = chicken;
count++;
}
//消费者消费
public synchronized Chicken pop(){
//判断能否消费
if (count==0) {
//通知生产者生产
}
Chicken chicken = chickens[count];
count--;
return chicken;
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
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) {
this.tv.play("快乐大本营播放中");
}else {
this.tv.play("抖音播放");
}
}
}
}
class Watcher extends Thread {
Tv tv;
public Watcher(Tv tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watch();
}
}
}
class Tv{
//演员演出 观众等待
//演出结束 观众观看
String voice; //节目
boolean flag=true;
//表演
public synchronized void play(String voice){
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演了"+voice);
//通知观众看
this.notifyAll();
this.voice=voice;
this.flag=!this.flag;
}
//观看
public synchronized void watch(){
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了"+voice);
//通知演员表演
this.notifyAll();
this.flag=!this.flag;
}
}
本文详细介绍了Java中的多线程核心概念,包括线程的创建、执行路径、并发控制以及线程间的同步。通过实例展示了通过继承Thread类和实现Runnable接口创建线程的方式,并分析了线程安全问题。此外,还探讨了线程礼让、强制执行、守护线程、线程通信、死锁以及Lock和synchronized的区别。同时,文章通过生产者消费者模型和电视播放与观看的例子,阐述了线程间的协作和通信机制。

1321

被折叠的 条评论
为什么被折叠?



