多线程详解

一、创建多线程

在主方法中调用run()方法和start方法的区别

(1)创建方式一:继承Thread

步骤

点击查看代码
package com.Tang.thread;
/*
创建线程方式一:
  (1)继承Thread类
  (2)重写run()方法
  (3)调用start开启进程
 */

//总结:线程开启不一定立即执行,由CPU调度执行
public class CreateThreadTest extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 200; i++) {
            System.out.println("Java学习"+i);
        }
    }

    public static void main(String[] args) {
        //main线程,主线程
        //创建一个线程对象
        CreateThreadTest threadTest = new CreateThreadTest();
        //调用start方法执行线程
        threadTest.start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("菜鸟学多线程"+i);
        }
    }
}

运行结果图


①练习:实现多线程同步下载图片
   实现前提得先下载commons-io的jar包,下面以下载commons-io-2.6为例:下载链接如下
https://archive.apache.org/dist/commons/io/binaries/
找到下图所示并点击下载


下载完成之后进行如下操作

出现如下图则表示操作成功

点击查看代码
package com.Tang.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class DownLoadImageTest extends Thread{
    private String url;//网络图片的地址
    private String name;//下载后保存的文件名

    public DownLoadImageTest(String url,String name) {
        this.name = name;
        this.url = url;
    }
    //下载图片线程的执行体
    @Override
    public void run() {
        Downloaginmage downloaginmage = new Downloaginmage();
        downloaginmage.Download(url,name);
        System.out.println("下载的文件名为:"+name);
    }

    public static void main(String[] args) {
        //创建三个线程
        DownLoadImageTest t1 = new DownLoadImageTest("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2Ftp09%2F210F2130512J47-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659248175&t=931e8197245cf794e1ad47cbed3adb36","1.jpg");
        DownLoadImageTest t2 = new DownLoadImageTest("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242306111155-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659248175&t=8da57973e2bd2626f51ce3394f4b486c","2.jpg");
        DownLoadImageTest t3 = new DownLoadImageTest("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242312005c1-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659248175&t=6e6dc08da17e84ebb5d0bef844c6c15d","3.jpg");
        //启动三个线程
        t1.start();
        t2.start();
        t3.start();
    }
}
//下载器
class Downloaginmage{
    //下载方法
    public void Download(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
运行结果图 由运行结果知,三个线程并不是按启动的顺序执行,而是同步执行,谁先下载完,就先输出谁

(2)创建方式二:实现Runnable接口(重点掌握)

步骤

点击查看代码
package com.Tang.thread;

public class CreateThreadTest02 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("Java学习"+i);
        }
    }

    public static void main(String[] args) {
        //创建Runnable接口的实现类
        CreateThreadTest02 createThreadTest02 = new CreateThreadTest02();
        //写法1:
//        Thread thread = new Thread(createThreadTest02);
//        thread.start();
        //写法2:
        new Thread(createThreadTest02).start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("菜鸟学习Java"+i);
        }
    }
}

运行结果图:


 并发所带来的线程不安全的问题

点击查看代码
package com.Tang.thread;
//多个线程操作同一个资源的情况下,线程不安全,容易造成数据紊乱
public class CreateThreadTest02 implements Runnable{
    //票数
    private int ticketnum = 10;
    
    @Override
    public void run() {
        while(true){
            if(ticketnum <= 0){
                break;
            }
            
            try {//模拟延时
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            //Thread.currentThread().getName():获取当前线程的名字
            System.out.println(Thread.currentThread().getName()+"-->抢到了第"+ticketnum--+"张票");
        }

    }
    public static void main(String[] args) {
        //创建Runnable接口的实现类
        CreateThreadTest02 createThreadTest02 = new CreateThreadTest02();

        new Thread(createThreadTest02,"小名").start();
        new Thread(createThreadTest02,"老师").start();
        new Thread(createThreadTest02,"黄牛党").start();
    }
}

运行结果图


①练习

点击查看代码
package com.Tang.thread;

import kotlin.reflect.jvm.internal.impl.descriptors.Visibilities;

public class Race implements Runnable{
    private static String winner;//唯一赢家
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            //模拟兔子休息:让兔子每走30步就睡5ms
            if(Thread.currentThread().getName().equals("兔子")&& i%30 ==0){
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断游戏是否结束
            if(isEnd(i))
                break;
            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
        }
    }
    //判断比赛是否结束
    private boolean isEnd(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();
    }
}

运行结果图

(3)实现callable接口(了解即可)

目的:实现网上图片的下载 步骤:

点击查看代码
package com.Tang.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

public class CreatThread03 implements Callable<Boolean> {
    private String url;//下载图片的地址
    private String name;//下载完成后保存的文件名

    public CreatThread03(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
        MyDownloader myDownloader = new MyDownloader();
        myDownloader.downloadermethod(url,name);
        System.out.println("下载的文件为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CreatThread03 t1 = new CreatThread03("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2Ftp09%2F210F2130512J47-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659248175&t=931e8197245cf794e1ad47cbed3adb36","1.jpg");
        CreatThread03 t2 = new CreatThread03("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242306111155-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659248175&t=8da57973e2bd2626f51ce3394f4b486c","2.jpg");
        CreatThread03 t3 = new CreatThread03("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242312005c1-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659248175&t=6e6dc08da17e84ebb5d0bef844c6c15d","3.jpg");
        //创建执行服务:
        ExecutorService ser = Executors.newFixedThreadPool(3);

        //提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);

        //获取结果
        Boolean aBoolean = r1.get();
        Boolean aBoolean2 = r2.get();
        Boolean aBoolean3 = r3.get();
        
        System.out.println(aBoolean);
        System.out.println(aBoolean2);
        System.out.println(aBoolean3);

        //关闭服务
        ser.shutdown();
    }
}
//下载器
class MyDownloader{
    //下载方法
    public void downloadermethod(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
运行结果图

二、静态代理

点击查看代码
package com.Tang.thread;
/*
静态代理模式总结
    真实对象和代理对象都要实现同一个接口
    代理对象要代理真实角色
好处:
    代理对象可以做很多真实对象做不了的事
    真实对象专注做自己的事
 */
public class StaticProxy {
    public static void main(String[] args) {
        You you = new You();//你要结婚
        //下面用到了lamda表达式,Thread实现了Runnable接口中只有一个run方法
        //所以可直接用()->表示该方法,并在该方法里输出一句话,然后运行该线程
        new Thread(()-> System.out.println("你爱他")).start();
        new WedCompany(you).HappyMarry();//传入代理对象(婚庆公司)需要代理的对象,并运行所代理的对象(你)里的方法
    }
}
interface Marry{
    void HappyMarry();
}
//真实角色
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("你要结婚了,你超开心");
    }
}
//代理角色
class WedCompany implements Marry{
    //代理的真实目标角色
    private Marry target;

    public WedCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();//代理角色在主角结婚前要干的事
        this.target.HappyMarry();//代理的目标角色结婚
        after();//代理角色在主角结婚后要干的事
    }
    private void before() {
        System.out.println("主角结婚前,婚庆公司要布置现场");
    }
    private void after() {
        System.out.println("主角结婚后,婚庆公司要收尾款");
    }


}
运行结果图

三、Lamda表达式

点击查看代码
package com.Tang.thread;

//以下五种方式可以任意选择一种,lambda表达式的四种简化方式可以根据实际情况任选一种
public class LambdaTest {
    //方式2.定义静态内部类
    static class Like2 implements Ilike{

        @Override
        public void love(int a) {
            System.out.println("静态内部类实现->"+a);
        }
    }
    public static void main(String[] args) {
        //方式3.局部内部类
        class Like3 implements Ilike{
            @Override
            public void love(int a) {
                System.out.println("局部内部类实现->"+a);
            }
        }
        //方式4.匿名内部类实现,没有类名,必须通过接口或者父类来实现
        Ilike ilike = new Ilike() {//注意这里new的是接口
            @Override
            public void love(int a) {
                System.out.println("匿名内部类实现->"+a);
            }
        };
        //方式5.lambda表达式简化
        Ilike ilike2 = (int a)->{
            System.out.println("lambda表达式简化->"+a);
        };
        //lambda表达式参数类型简化:若有多个参数,参数类型要简化必须都简化
        Ilike ilike3 = (a)->{
            System.out.println("lambda表达式参数类型简化->"+a);
        };
        //lambda表达式小括号简化:若有多个参数,则必须有括号此时该简化方式则不可取
        Ilike ilike4 = a->{
            System.out.println("lambda表达式小括号简化->"+a);
        };
        //lambda表达式花括号简化:如果接口方法中只有一行代码(love方法中只有一行输出代码)可以简化,否则不能简化花括号
        Ilike iLike5 = a-> System.out.println("lambda表达式花括号简化->"+a);
        
        
        Like like = new Like();
        like.love(1);

        Like2 like2 = new Like2();
        like2.love(2);

        Like3 like3 = new Like3();
        like3.love(3);

        ilike.love(4);

        ilike2.love(5);

        ilike3.love(6);

        ilike4.love(7);

        iLike5.love(8);

    }
}
//定义函数式接口:一个接口中只有唯一 一个方法,这也是写lambda的前提
interface Ilike{
    void love(int a);
}
//方式1.定义实现类
class Like implements Ilike{

    @Override
    public void love(int a) {
        System.out.println("外部定义类实现接口->"+a);
    }
}
运行结果图

四、线程停止

点击查看代码
package com.Tang.thread;

public class ThreadStopTest implements Runnable{
    //1.线程中定义线程体使用的标识
    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) {
        ThreadStopTest stopTest = new ThreadStopTest();
        new Thread(stopTest).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            //终止线程
            if(i == 900){
                stopTest.stop();
                System.out.println("该线程已经停止了");
            }
        }
    }
}

运行结果图

五、线程休眠

(1)目的:实现10秒倒计时

点击查看代码
package com.Tang.thread;

public class SleepThreadTest{

    public static void tenDown() throws InterruptedException {
        int num = 10;
        while(true){
            Thread.sleep(500);
            System.out.println(num--);
            if(num <= 0){
                break;
            }
        }
    }

    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果图

(2)目的:实现当前系统时间的倒计时

点击查看代码
package com.Tang.thread;


import java.text.SimpleDateFormat;
import java.util.Date;

public class SleepThreadTest implements Runnable{
    public static void main(String[] args) {
        SleepThreadTest sleepThreadTest = new SleepThreadTest();
        new Thread(sleepThreadTest).start();
    }

    @Override
    public void run() {
        //获取当前系统时间
        Date startData = new Date(System.currentTimeMillis());
        while (true){
            try {
                Thread.sleep(1000);
                //输出当前系统的时间
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startData));

                startData = new Date(System.currentTimeMillis());//更新当前系统时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果图

六、线程礼让

线程礼让:就是让当前在CPU上运行的线程结束运行,然后与其它线程一起再次竞争CPU资源
点击查看代码
package com.Tang.thread;

public class YieldTest {
    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() + "-->start");
        Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName() + "-->end");
    }
}
运行结果图

礼让失败


礼让成功

七、线程插队join

目的:实现当某一个线程运行join之后就会让该线程一直运行,直到该线程运行完之后,其他线程才能继续运行
点击查看代码
package com.Tang.thread;

public class JoinThreadTest implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("vip线程来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        JoinThreadTest joinThreadTest = new JoinThreadTest();
        Thread thread = new Thread(joinThreadTest);
        for (int i = 0; i < 50; i++) {
            if(i == 25) {
                //线程的开始如果写在循环之外,一开始会和主线程同步运行
                //当i=25之后main线程停止运行,然后当执行了join的线程运行结束之后,main线程才继续运行
                thread.start();

                thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

运行结果图

八、观测线程状态


目的:实现进程状态在代码中的显示

点击查看代码
package com.Tang.thread;

public class StateThreadTest{
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 10; i++) {//线程等待2秒
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("。。。。");//到这线程运行完毕
        });
        Thread.State state = thread.getState();
        System.out.println(state);//new
        //观察启动后的线程的状态
        thread.start();
        state = thread.getState();
        System.out.println(state);

        while(state != Thread.State.TERMINATED){
            try {
                Thread.sleep(100);
                state = thread.getState();//更新状态
                System.out.println(state);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

运行结果图

九、线程优先级

源码分析

点击查看代码
package com.Tang.thread;

public class PriorityThreadTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority,"A线程");
        Thread t2 = new Thread(myPriority,"B线程");
        Thread t3 = new Thread(myPriority,"C线程");
        Thread t4 = new Thread(myPriority,"D线程");
        Thread t5 = new Thread(myPriority,"E线程");
        Thread t6 = new Thread(myPriority,"F线程");
        //先设置优先级在启动,如没有设置优先级则默认为5
        t1.start();

        t2.setPriority(Thread.MIN_PRIORITY);//MIN_PRIORITY=1
        t2.start();

        t3.setPriority(9);
        t3.start();

        t4.setPriority(3);
        t4.start();

        t5.setPriority(6);
        t5.start();

        t6.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
        t6.start();


    }
}
class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
    }
}
运行结果图

十、守护线程

点击查看代码
package com.Tang.thread;

public class daemonTheadTest {
    public static void main(String[] args) {
        God god = new God();
        You1 you1 = new You1();

        Thread godthread = new Thread(god);
        godthread.setDaemon(true);//默认是false,表示是用户线程,正常的线程都是用户线程
        godthread.start();//上帝守护线程启动
        new Thread(you1).start();
    }
}
class God implements Runnable{

    @Override
    public void run() {
        while(true){
            System.out.println("上帝一直保佑你");
        }
    }
}
class You1 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你一生都很快乐"+i);
        }
    }
}
运行结果图 虽然守护线程内的循环是一直运行的不会停止,但是当用户线程结束之后,守护线程也会结束(结果图中不是立马结束,主要是因为守护线程的停止也需要时间)

十一、线程同步

1.三大线程不安全案例

(1)买票不安全问题

点击查看代码
package com.Tang.thread;
//不安全的买票
//出现负数票:当还剩一张票的时候三个会都去拿,
// 假设第一个人抢到了第一张票,第二个人可能就是第0张票,第三个人就是第-1张票

public class UnSafeTicket {
    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 num = 10;
    boolean flag = true;//线程结束的标志
    @Override
    public void run() {
        while(flag){
            buy();
        }
    }
    public void buy(){
        if(num <= 0){//没有票之后停止运行
            this.flag = false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"抢到了第"+num--+"张票");

    }
}
运行结果图

改成线程安全之后代码如下:

点击查看代码
package com.Tang.thread;
public class UnSafeTicket {
    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 num = 10;
    boolean flag = true;//线程结束的标志
    @Override
    public void run() {
        while(flag){
            buy();
        }
    }
    //在方法前加synchronized,则该方法就被称为同步方法,
    // synchronized锁的是this,在本例子中就是锁的BuyTicket这个类
    public synchronized void buy(){
        if(num <= 0){//没有票之后停止运行
            this.flag = false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"抢到了第"+num--+"张票");

    }
}
运行结果图

(2)两个人去银行取钱

点击查看代码
package com.Tang.thread;

public class UnSafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"私房钱账户");
        new Bank(account,50,"你").start();//你取50万
        new Bank(account,100,"youGirlFrend").start();//你对象在你的私房钱账户中取100万

    }
}
//账户
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 drawingMoney;
    //全款账户名
    String name;
    public Bank(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    int nowMoney;//现在手里的钱
    //取钱
    @Override
    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 += drawingMoney;
        System.out.println(account.name+"账户余额为:"+account.money);
        //由于继承的影响Thread.currentThread().getName()这个写法与this.getName()写法一样
        System.out.println(this.getName() + "手里的钱为:"+nowMoney);
    }
}
运行结果图


改成安全之后的代码如下:
锁的对象就是变化的量,需要增删改的对象

点击查看代码
package com.Tang.thread;

public class UnSafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"私房钱");
        new Bank(account,50,"你").start();//你取50万
        new Bank(account,100,"youGirlFrend").start();//你对象在你的私房钱账户中取100万

    }
}
//账户
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 drawingMoney;
    //全款账户名
    String name;
    public Bank(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    int nowMoney;//现在手里的钱
    //取钱
    @Override
    public void run() {
        //此时变化的是银行卡账户,所以对银行卡进行上锁,
        //如果对run()方法上锁,实际上锁对象时Bank,而这个对象并没有进行增删改
        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 += drawingMoney;
            System.out.println(account.name+"账户余额为:"+account.money);
            //由于继承的影响Thread.currentThread().getName()这个写法与this.getName()写法一样
            System.out.println(this.getName() + "手里的钱为:"+nowMoney);
        }
    }
}
运行结果图

(3)线程不安全的集合

点击查看代码
package com.Tang.thread;

import java.util.ArrayList;

public class UnSafeList {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
      
        System.out.println(list.size());
    }
}

运行结果图


改成线程安全代码之后:

点击查看代码
package com.Tang.thread;

import java.util.ArrayList;
import java.util.List;

public class UnSafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){//list是变化的量,所以对其进行上锁操作
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

运行结果图

拓展知识

CopyOnWriteArrayList为JUC下线程安全的
点击查看代码
package com.Tang.thread;

import java.util.concurrent.CopyOnWriteArrayList;

public class JUCTest {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());//这里没有对list进行同步锁,最终发现CopyOnWriteArrayList是线程安全的
            }).start();
        }
        System.out.println(list.size());
    }
}

运行结果图

十二、死锁

点击查看代码
package com.Tang.thread;

public class DeadLockTest {
    public static void main(String[] args) {
        //两个线程都互相拥有对方需要的资源,然后形成僵持,就被称为死锁
        MakeUp g1 = new MakeUp(0, "灰姑凉");//先拿口红,一秒后拿镜子
        MakeUp g2 = new MakeUp(1, "白雪公主");//先拿镜子,两秒后拿口红

        g1.start();
        g2.start();
    }
}
//口红
class Lipstick{

}
//镜子
class Mirror{

}

class MakeUp extends Thread{
    //需要的资源只有一份,用static来保证只有一份
    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() {
        //化妆
        makeup();
    }
    public void makeup(){
        if(this.choice == 0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(this.girlName + "拿到口红锁");
                try {
                    Thread.sleep(1000);
                    synchronized (mirror){//一秒之后获得镜子的锁
                        System.out.println(this.girlName + "拿到镜子的锁");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }else{
            synchronized (mirror){//获得镜子的锁
                System.out.println(this.girlName + "拿到镜子锁");
                try {
                    Thread.sleep(2000);
                    synchronized (lipstick){//两秒之后获得口红的锁
                        System.out.println(this.girlName + "拿到口红的锁");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
运行结果图

由运行结果知当灰姑凉拿到镜子,白雪公主拿到口红之后又分别想要对方的资源导致进程无法向下推进,引起程序无法结束


解除死锁方法

点击查看代码
package com.Tang.thread;

public class DeadLockTest {
    public static void main(String[] args) {
        //两个线程都互相拥有对方需要的资源,然后形成僵持,就被称为死锁
        MakeUp g1 = new MakeUp(0, "灰姑凉");//先拿口红,一秒后拿镜子
        MakeUp g2 = new MakeUp(1, "白雪公主");//先拿镜子,两秒后拿口红

        g1.start();
        g2.start();
    }
}
//口红
class Lipstick{

}
//镜子
class Mirror{

}

class MakeUp extends Thread{
    //需要的资源只有一份,用static来保证只有一份
    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() {
        //化妆
        makeup();
    }
    public void makeup(){
        if(this.choice == 0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(this.girlName + "拿到口红锁");
                try {
                    Thread.sleep(1000);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (mirror){//一秒之后获得镜子的
                System.out.println(this.girlName + "拿到镜子的锁");
            }
        }else{
            synchronized (mirror){//获得镜子的锁
                System.out.println(this.girlName + "拿到镜子锁");
                try {
                    Thread.sleep(2000);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (lipstick){//两秒之后获得口红的锁
                System.out.println(this.girlName + "拿到口红的锁");
            }
        }
    }
}
运行结果图

十三、Lock锁

加Lock锁之前
点击查看代码
package com.Tang.thread;

public class LockTest {
    public static void main(String[] args) {
        LockTest2 lockTest2 = new LockTest2();

        new Thread(lockTest2).start();
        new Thread(lockTest2).start();
        new Thread(lockTest2).start();
    }

}
class LockTest2 implements Runnable{
    int numtick = 10;
    @Override
    public void run() {
        while (true){
            if(numtick <= 0){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(numtick--);
        }
    }

}
加Lock锁之前运行结果图


加Lock锁之后

点击查看代码
package com.Tang.thread;

import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
    public static void main(String[] args) {
        LockTest2 lockTest2 = new LockTest2();

        new Thread(lockTest2).start();
        new Thread(lockTest2).start();
        new Thread(lockTest2).start();
    }

}
class LockTest2 implements Runnable{
    int numtick = 10;
    //定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){

            try{
                lock.lock();//加锁
                if(numtick <= 0){
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(numtick--);
            }finally {
                lock.unlock();//解锁
            }

        }
    }

}
加锁之后运行结果图

十四、生产者消费者模型

1.管程法解决

点击查看代码
package com.Tang.thread;

public class ProductConsumerTest {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        Product product = new Product(container);
        Consumer consumer = new Consumer(container);

        product.start();
        consumer.start();
    }
}

//生产者
class Product extends Thread{
    SynContainer container;

    public Product(SynContainer container) {
        this.container = container;
    }
    //生产鸡
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));//往容器里生产鸡
            System.out.println("生产了" + 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 Chicken {
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区
class SynContainer{
    //存储鸡的地方
    Chicken[] chickens = new Chicken[10];
    //容量计数器
    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.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop(){
        //判断容器中是否有鸡
        if(count == 0){//没有鸡了
            //等待生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //还有鸡的话,消费者就消费这只鸡
        count --;
        Chicken chicken = chickens[count];


        //当消费者消费鸡之后就可以通知生产者生产鸡
        this.notifyAll();
        return chicken;

    }
}
运行结果图

2.信号灯法解决

点击查看代码
package com.Tang.thread;

public class ProductConsumerTest2 {
    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.player("快乐大本营");
            }else{
                this.tv.player("抖音直播");
            }
        }
    }
}

//消费者-->观众
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.watcher();
        }
    }
}

//产品-->节目
class TV{
    //演员表演,观众等待 T
    //观众观看,演员等待 F
    String voice;//节目
    boolean flag = true;
    //演员表演
    public synchronized void player(String voice){
        if(!flag){//观众观看时,演员等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:"+voice);
        //通知观众观看
        this.notifyAll();
        this.voice = voice;
        this.flag = !flag;
    }
    public synchronized void watcher(){
        if(flag){//演员表演时观众等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("观众观看了:"+voice);
        //通知演员表演
        this.notifyAll();
        this.flag = !flag;
    }
}
运行结果图

十五、线程池

点击查看代码
package com.Tang.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PoolTest {
    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.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
运行结果图

posted @ 2022-07-12 20:12  剑断青丝ii  阅读(101)  评论(0编辑  收藏  举报