zoukankan      html  css  js  c++  java
  • 多线程

    线程简介

    多任务:边吃饭边玩手机,同时做多件事(但是实际上是分时进行的,大脑分时处理,时间交替很快)
    多线程:多车道,多条线路同时执行任务

    普通方法调用和多线程
    直接调用run和调用start函数的不同,直接调用run函数无法实现多线程

    在操作系统中运行的程序就是进程(qq,播放器,游戏,IDE),播放视频时有声音,图像,字幕,这些就是由不同的线程控制

    进程与线程
    说起进程,就不得不说下程序,程序是数据和指令的有序集合,其本身没有任何运行的含义,是一个静态的概念
    而进程是执行程序的一次执行过程,它是一个动态的概念,进程是系统分配资源的单位(程序跑起来才叫一个进程)
    通常一个进程可以包含若干个线程,一个进程至少包含一个线程,线程是CPU调度和执行的单位

    注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器,如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换得很快,所以就有同时执行的错局

    核心概念
    线程就是独立的执行路径
    在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc(垃圾回收)线程
    main()称之为主线程,为系统的入口,用于执行整个程序
    在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
    对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
    线程会带来额外的开销,如CPU调度时间,并发控制开销(让线程排队执行)
    每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

    线程实现

    线程创建
    Thread Runnable Callable

    main函数是自己写的叫用户线程
    gc线程是JVM给的,叫守护线程

    线程创建的3种方式

    1 继承Thread类(重点)
    不建议使用:为了避免OOP单继承局限性
    Thread类本身实现了Runnable接口
    ①自定义线程类继承Thread类
    ②重写run方法,编写程序执行体
    ③创建线程对象,调用start()方法启动线程

    总结:线程开启不一定立即执行,由CPU调度执行,直接调用run方法相当于调用普通方法,不会创建新的线程

    /**
     *@className: TestThread
     *@description: 创建线程方式1:继承Thread类,重写run()方法,调用start开启线程
     * 总结: 线程开启不一定立即执行,由cpu调度执行
     *@author: jj.liu
     *@create: 2021-10-12 09:53
     */
    public class TestThread1 extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                System.out.println("run()方法线程体正在执行-----------------"+i);
            }
        }
        public static void main(String[] args) {
            TestThread1 thread = new TestThread1();
            //调用start()方法开启线程:多条执行路径,主线程和子线程并行交替执行
            thread.start();
            //调用run()方法开启线程:主线程先执行run方法再执行main()
            //thread.run();
    
            for (int i = 0; i < 1000; i++) {
                System.out.println("main()方法主线程正在执行。。。。。。。。。。。。。"+i);
            }
        }
    }

     案例:多线程下载图片

    需要用到commons-io2.6这个jar包,可以自己去百度下载
    在这里插入图片描述
    然后把包拷贝到我们的工程下面,右键 Add as library

    因为我们需要用到commons-io包里面的FileUtils函数
    在这里插入图片描述

    /**
     *@className: TestThread2
     *@description: 利用多线程实现多个网络图片同时下载
     *@author: jj.liu
     *@create: 2021-10-12 10:40
     */
    public class TestThread2 extends Thread{
    
        String url;
        String name;
    
        public TestThread2(String url,String name){
            this.url = url;
            this.name = name;
        }
        @Override
        public void run() {
            WebDownLoader downloader = new WebDownLoader();
            downloader.donwloader(url,name);
        }
        public static void main(String[] args) {
            TestThread2 t1 = new TestThread2("https://tenfei02.cfp.cn/creative/vcg/800/new/VCG211164262326.jpg","1.jpg");
            TestThread2 t2 = new TestThread2("https://t7.baidu.com/it/u=1392863136,806273060&fm=193&f=GIF","2.jpg");
            TestThread2 t3 = new TestThread2("https://t7.baidu.com/it/u=2301872827,3431176467&fm=193&f=GIF","3.jpg");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    package com.agentliu.demo01;
    import org.apache.commons.io.FileUtils;
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    
    /**
     *@className: WebDownLoader
     *@description: 图片下载器
     *@author: jj.liu
     *@create: 2021-10-12 17:55
     */
    public class WebDownLoader {
    
        public void donwloader(String url,String name)  {
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
                System.out.println("下载图片成功,下载图片为:"+name);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("下载器下载图片失败");
            }
        }
    }

    结果 如图。

    2、实现Runnable接口

    java是单继承,推荐使用Runnable接口,方便同一个独享被多个线程使用
    避免了单继承的局限性:即在Java中一个类只能使用extends继承一个父类.,如果继承多个父类,而父类有同名方法时就不知道调用哪一个方法了,另外会是两个类的耦合性增加,如果父类有改动时会直接影响子类

    ① 定义MyRunnable类实现Runnabke接口
    ②实现Run()方法,编写程序执行体
    ③创建线程对象,调用start()方法启动线程

    /**
     *@className: TestThread3
     *@description: 实现Runable接口
     *@author: jj.liu
     *@create: 2021-10-12 11:41
     */
    public class TestThread3 implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                System.out.println("run()方法线程体正在执行-----------------"+i);
            }
        }
        public static void main(String[] args) {
            TestThread3 thread3 = new TestThread3();
            Thread thread = new Thread(thread3);
            thread.start();
    
            for (int i = 0; i < 1000; i++) {
                System.out.println("main()方法主线程正在执行。。。。。。。。。。。。。"+i);
            }
        }
    }

    初识并发问题

    多个线程操作同一个资源的情况下出现不同的线程抢到同一张票,线程不安全,数据紊乱

    /**
     *@className: TestThread4
     *@description: 多线程并发抢票问题
     *@author: jj.liu
     *@create: 2021-10-12 14:29
     */
    public class TestThread4 implements Runnable{
        private int ticketNum = 10 ;
        @Override
        public void run() {
            while (true){
                if(ticketNum <= 1){
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNum-- +"张票");
            }
        }
        public static void main(String[] args) {
            TestThread4 thread1 = new TestThread4();
            new Thread(thread1,"小明").start();
            new Thread(thread1,"老王").start();
            new Thread(thread1,"黄牛").start();
        }
    }

    结果如果:

     案例:龟兔赛跑:

    /**
     *@className: Race
     *@description: 龟兔赛跑,模拟兔子乌龟赛跑,兔子中途休息,乌龟赢得比赛
     *@author: jj.liu
     *@create: 2021-10-12 16:58
     */
    public class Race implements Runnable{
        public String winer;
        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                //模拟兔子中途休息
                if(Thread.currentThread().getName().equals("兔子")&& i%10==0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                boolean flag = stopGame(i);
                //如果比赛结束,停止循环
                if(flag){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
            }
        }
        //判断是否完成比赛
        public boolean stopGame(int i){
            //判断是否有胜利者
            if(winer != null){
                return true;
            }{
                if ( i == 100) {
                    winer = Thread.currentThread().getName();
                    System.out.println("winer is:" + winer);
                    return true;
                }
            }
            return false;
        }
        public static void main(String[] args) {
            Race race = new Race();
            new Thread(race,"兔子").start();
            new Thread(race,"乌龟").start();
        }
    }

    结果如图:兔子在第10秒休息,乌龟赢得比赛。

    3、 实现Callable接口

    1.实现Callable接口,需要返回值类型

    2.重写call方法,需要抛出异常
    3.创建目标对象
    4.创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
    5.提交执行:Future result1 = ser.submit(t1);
    6.获取结果: boolean r1 = result1.get()
    7.关闭服务:ser.shutdownNow();

    /**
     * @className: TestCallableThread
     * @description: 实现callable接口
     * @author: jj.liu
     * @create: 2021-10-12 17:45
     */
    public class TestCallableThread implements Callable {
        String url;
        String name;
        public TestCallableThread(String url, String name) {
            this.url = url;
            this.name = name;
        }
        @Override
        public Boolean call() {
            WebDownLoader downloader = new WebDownLoader();
            downloader.donwloader(url, name);
            return true;
        }
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            TestCallableThread t1 = new TestCallableThread("https://tenfei02.cfp.cn/creative/vcg/800/new/VCG211164262326.jpg", "1.jpg");
            TestCallableThread t2 = new TestCallableThread("https://t7.baidu.com/it/u=1392863136,806273060&fm=193&f=GIF", "2.jpg");
            TestCallableThread t3 = new TestCallableThread("https://t7.baidu.com/it/u=2301872827,3431176467&fm=193&f=GIF", "3.jpg");
            //创建线程池,放3个线程
            ExecutorService ser = Executors.newFixedThreadPool(3);
            //提交执行线程
            Future<Boolean> r1 = ser.submit(t1);
            Future<Boolean> r2 = ser.submit(t2);
            Future<Boolean> r3 = ser.submit(t3);
            //获取执行结果
            r1.get();
            r2.get();
            r3.get();
            //关闭线程池
            ser.shutdownNow();
        }
    }

     

    Lamda表达式

    任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

    对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

    详见:https://www.cnblogs.com/ljjdyz/p/15402986.html

    线程之静态代理模式

    详见:https://www.cnblogs.com/ljjdyz/p/15405450.html

    线程状态

    五大状态

    创建状态:new
    就绪状态:start

    运行状态:run
    阻塞状态:sleep(sleep只是其中一个)
    死亡状态:正常执行完

    线程方法

    方法 说明
    setPriority(int newPriority) 更改线程的优先级
    static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
    void join() 等待该线程终止
    static void yield() 暂停当前正在执行的线程对象,并执行其他线程
    void interrupt() 中断线程,别用这个方式
    boolean isAlive() 测试线程是否处于活动状态

    停止线程

    不推荐使用jdk提供的stop(),destory()方法,建议使用一个标志位进行终止变量,当flag=false,则线程终止运行

    public class Teststop implements Runnable {
    //1.线程中定义线程体使用的标识
    private boolean flag = true;
    @Override
    public void run (){
    / /2 .线程体使用该标识
    while (flag) {
    systepaoit.println ( "run. . . Thread" );
    }
    }
    //3.对外提供方法改变标识
    public void stop(){
    this.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 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("run线程停止了!");
                }
            }
        }
    }

    线程休眠(sleep)

    sleep(时间)指定当前线程阻塞的毫秒数;

    sleep存在异常InterruptedException;
    sleep时间达到后线程进入就绪状态;
    sleep可以模拟网络延时(放大问题的发生性,比如多线程卖票,一票多卖),倒计时等。
    每一个对象都有一个锁,sleep不会释放锁;

    package MultiThread;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.logging.SimpleFormatter;
    
    //模拟倒计时
    public class TestSleep implements Runnable {
    
        public static void main(String[] args) throws InterruptedException {
            tenDown();
            //打印当前系统时间
            Date startTime=new Date(System.currentTimeMillis());
            while(true)
            {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime=new Date(System.currentTimeMillis());
    
            }
        }
        public static void tenDown() throws InterruptedException {//模拟倒计时
            int num=10;
            while(true)
            {
              Thread.sleep(1000);
                System.out.println(num--);
                if(num<=0)
                {
                    break;
                }
            }
        }
    
        @Override
        public void run() {
        }
    }

    线程礼让(yield)

    礼让线程,让当前正在执行的线程暂停,但不阻塞
    将线程从运行状态转为就绪状态
    让CPU重新调度,礼让不一定成功

    package MultiThread;
    
    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()+"线程结束执行");
        }
    }

    礼让成功:

     礼让失败:

    其实这里注释掉yield,还是会出现a先执行或者b先执行这种情况,因为是多线程嘛

    线程强制执行(join)

    join合并线程,待此线程执行完毕之后,在执行其他线程,其他线程阻塞,可以想象成插队

    package MultiThread;
    
    public class TestJoin implements Runnable {
    
        public static void main(String[] args) throws InterruptedException {
            TestJoin testJoin=new  TestJoin();
            Thread thread=  new Thread( testJoin);
            thread.start();
    
            for (int i = 0; i < 50; i++) {
              if(i==25)
              {
                  thread.join();
              }
                System.out.println("主线程"+i);
            }
        }
        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                System.out.println("join线程"+i);
            }
        }
    }

     注意join是通过new Thread 对象来调用的,而sleep和yield是通过Thread直接调用

    观测线程状态

    /**
     *@className: TestStatus
     *@description: 测试观察线程运行状态
     *@author: jj.liu
     *@create: 2021-10-22 11:20
     */
    public class TestStatus {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(()-> {
                for (int i = 0; i < 5; i++) {
                    try {
                        //模拟5秒倒计时
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("////////////循环结束");
            });
            //线程刚被创建时得状态
            Thread.State state = thread.getState();
            System.out.println(state);//new
    
            //调用start方法后,线程进行运行状态
            thread.start();//RUNNABLE
            state = thread.getState();
            System.out.println(state);
    
            //只要线程不停止,输出阻塞时得状态
            while (state != Thread.State.TERMINATED){
                Thread.sleep(100);
                state = thread.getState();
                System.out.println(state);
            }
    //        thread.start(); //线程死亡状态以后就不能再重新再执行
        }
    }

    线程优先级

     优先级高的不一定会先执行

    package 多线程使用;
    
    public class Testpriority {
        public static void main(String[] args) {
    //        主线程默认优先级
            System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
            Mypriority mypriority = new Mypriority();
    
            Thread th_01 = new Thread(mypriority,"二号线程");
            Thread th_02 = new Thread(mypriority,"三号线程");
            Thread th_03 = new Thread(mypriority,"一号");
            Thread th_04 = new Thread(mypriority,"四号");
            Thread th_05 = new Thread(mypriority,"五号");
    //        先设置优先级
            th_01.start();
    
            th_02.setPriority(1);
            th_02.start();
    
            th_03.setPriority(4);
            th_03.start();
    
            th_04.setPriority(Thread.MAX_PRIORITY);
            th_04.start();
    
            th_05.setPriority(7);
            th_05.start();
        }
    }
    class Mypriority implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        }
    }

     守护线程

    在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 

    用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

    只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
    Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

    User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

     ​ Thread thread = new Thread(god);
    ​ thread.setDaemon(true);//设置守护线程 true
    ​ thread.start();

    /**
     * @author: codeyuaiaio
         守护线程
     */
    public class ThreadDaemon {
        public static void main(String[] args) {
            God god = new God();
            You you = new You();
    
            Thread thread = new Thread(god);
            //默认是false表示用户线程,正常的线程都是用户线程....
           thread.setDaemon(true);
            //上帝守护线程启动
            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 <= 100; i++) {
                System.out.println("我还开开心心的活着"+i+"");
            }
            System.out.println("=====goodbye world=====");
        }
    }

    线程同步机制 synchronized

    并发同一个对象多个线程同时操作

     队列+ 锁才能保证线程同步的安全性

     线程不安全案例:

    案例1:三人同时买票

    package com.agentliu.sync;
    
    /**
     *@className: TestStatus
     *@description: 不安全的买票
     *@author: jj.liu
     *@create: 2021-10-22 16:21
     */
    public class UnSafeByTicket  {
    
        public static void main(String[] args) {
            BuyTicket t = new BuyTicket();
            Thread t1 = new Thread(t,"老王");
            Thread t2 = new Thread(t,"");
            Thread t3 = new Thread(t,"黄牛党");
    
            t1.start();
            t2.start();
            t3.start();
        }
    
    }
    
    class BuyTicket implements Runnable{
    
        int ticketNumber = 10;
        Boolean flag = true;
    
        @Override
        public void run() {
            buy();
        }
        void buy(){
            while (flag){
                if(ticketNumber <= 0){
                    flag = false;
                    break ;
                }
                System.out.println(Thread.currentThread().getName()+"买到了第---》"+ticketNumber+"张票");
                ticketNumber --;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

     案例2:不安全的集合ArrayList

    package com.agentliu.sync;

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

    /**
    *@className: UnSafeList
    *@description: 线程不安全的集合
    *@author: jj.liu
    *@create: 2021-10-22 17:03
    */
    public class UnSafeList {
    public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 10000; i++) {
    new Thread(()->{
    list.add("a");
    }).start();
    }
    try {
    //休息2秒,放大问题的发生性,防止for循环还没完就打印集合大小
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(list.size());
    }
    }

     案例3: 线程不安全的取钱

    
    
    package com.agentliu.sync;

    /**
    *@className: UnSafeByBank
    *@description: 线程不安全的取钱
    *@author: jj.liu
    *@create: 2021-10-25 14:59
    */
    public class UnSafeByBank {
    public static void main(String[] args) {
    Accout accout = new Accout(100);
    WithDraw t1 = new WithDraw(accout,100,"你女朋友");
    WithDraw t2 = new WithDraw(accout,50,"你");
    t1.start();
    t2.start();
    }
    }

    //账户
    class Accout{

    int money;

    public Accout(int money){
    this.money = money;
    }

    }
    //取钱
    class WithDraw extends Thread{
    //账户
    Accout accout;
    //取钱金额
    int withDrawMoney;
    //余额
    int nowMoney;
    //取款人
    String withDrawName;

    public WithDraw(Accout accout,int withDrawMoney,String withDrawName){
    this.accout = accout;
    this.withDrawMoney = withDrawMoney;
    this.withDrawName = withDrawName;
    }

    @Override
    public void run(){
    if(withDrawMoney - accout.money > 0){
    System.out.println(Thread.currentThread().getName()+"钱不够取,取不了");
    return;
    }
    // sleep放大问题的发生性
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    //卡内余额 = 余额-你取的钱
    accout.money = accout.money-withDrawMoney;
    System.out.println(withDrawName+"取的钱为:"+withDrawMoney);
    System.out.println(withDrawName+"账户余额为:"+accout.money);
    }
    }
     

    synchronize线程锁

    每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

    synchronize同步方法

    在方法上加修饰词synchronize锁的是当前对象

     

    同步方法的弊端性能低 因为只需要修改的变量资源才需要上锁

     

    synchronize同步代码块

    同步代码块可以锁任意指定对象

     synchronized代码块同步锁的例子模拟银行操作: 需要锁的对象为账户,账户的金额在发生变化。如果将synchronized放到run()方法上,那么上锁的是this当前类。

     

     synchronized同步方法的例子模拟买票

    **synchronized 代码块同步锁的例子Arraylist **

    ArrayList是线程不安全的,因为多线程在操作list的空间会同时写到同一块内存位置,然后会覆盖掉之前的内容

     JUC并发下的线程安全的集合CopyOnWriteArrayList

    package 多线程使用.线程安全;
    
    import java.util.concurrent.CopyOnWriteArrayList;
    
    //测试juc安全类型的集合
    public class TestJuc {
        public static void main(String[] args) {
            CopyOnWriteArrayList list = new CopyOnWriteArrayList<String>();
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    list.add(Thread.currentThread().getName());
                }).start();
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(list.size());
        }
    }

    死锁问题

    死锁: 多个线程互相持有对方的需要的资源 ,然后形成互相僵持

    package com.agentliu.lock;
    
    /**
     *@className: DeadLock
     *@description: 死锁的形成
     *@author: jj.liu
     *@create: 2021-10-25 17:20
     */
    public class DeadLock {
        public static void main(String[] args) {
            Makeup t1 = new Makeup(0,"小美");
            Makeup t2 = new Makeup(1,"小丽");
            t1.start();
            t2.start();
        }
    }
    //口红
    class Lipstick{}
    //镜子
    class Mirror{}
    
    //化妆
    class Makeup extends Thread{
        //用static来保证镜子和口红都只有一份
        static Lipstick l = new Lipstick();
        static Mirror m = 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 (l){
                    Thread.sleep(1000);
                    System.out.println(girlName+"获得口红的锁,她需要镜子");
                    synchronized (m){
                        System.out.println(girlName+"获得镜子的锁,化妆完成");
                    }
                }
            }else{
                //第二个人获得镜子的锁,她需要口红
                synchronized (m){
                    Thread.sleep(2000);
                    System.out.println(girlName+"获得镜子的锁,她需要口红");
                    synchronized (l){
                        System.out.println(girlName+"获得口红的锁,化妆完成");
                    }
                }
            }
        }
    }

     

    释放掉对方需要的对象

     

    Lock锁

    在juc并发包下

    //ReentrantLock :可重入锁

    // 定义lock

    private ReentrantLock re = new ReentrantLock();

    synchronized与Lock的对比

     

    线程通信

    模拟生产者和消费者

     生产者消费者模型->利用缓冲区解决:管程法

    package 多线程使用.线程协作;
    //测试:生产者消费者模型->利用缓冲区解决:管程法
    //生产者 消费者 产品 缓冲区
    public class TestPc {
        public static void main(String[] args) {
            SynContainer sy =  new SynContainer();
            new Productor(sy).start();
            new Consumer(sy).start();
        }
    }
    //生产者
    class Productor extends  Thread{
        private SynContainer sy;
        Productor (SynContainer sy){
            this.sy=sy;
        }
        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                this.sy.push(new Chicken(i));
                System.out.println("正在生产第:"+i+"只鸡");
            }
        }
    }
    //消费者
    class Consumer extends Thread{
        private SynContainer sy;
        Consumer (SynContainer sy){
            this.sy=sy;
        }
    
        @Override
        public void run() {
            for (int i = 1; i < 100; i++) {
                System.out.println("正在消费第:"+this.sy.pop().id+"只鸡");
    
            }
        }
    }
    //产品
    class Chicken{
        int id;
    
        public Chicken(int id) {
            this.id = id;
        }
    }
    //缓冲区
    class SynContainer{
        Chicken[] ch=new Chicken[10];
        int cont=0;
        public synchronized void push(Chicken chicken){
            if(cont==ch.length){
    //            生产满了等待
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    //     生产产品
            ch[cont]=chicken;
            cont++;
     //        通知消费者消费
            this.notifyAll();
        }
        public synchronized  Chicken pop(){
    //        没有产品了等待生产者
            if(cont==0){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
    //        消费通知生产者生产
            cont--;
            Chicken chicken = ch[cont];
    
            this.notifyAll();
    
            return chicken;
        }
    }

    信号灯发

    package 多线程使用.线程协作;
    
    public class Test_pc2 {
        public static void main(String[] args) {
            TV tv = new TV();
            new Player(tv).start();
            new Watcher(tv).start();
        }
    }
    class Player extends Thread{
        TV t;
        Player (TV t){
            this.t=t;
        }
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i%3==0){
                    t.paly("演员表示节目:"+i);
                }else t.paly("正在播放广告"+i);
    
            }
        }
    }
    class Watcher extends Thread{
        TV t;
        Watcher (TV t){
            this.t=t;
        }
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                t.watch();
            }
        }
    }
    class TV{
    //    表演的节目
        String voice;
        boolean flag=true;
    
        public synchronized void paly(String voice){
            if(!flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("演员表演:"+voice);
            flag=!flag;
    //        通知观众线程观看
            this.voice=voice;
            this.notifyAll();
        }
        public synchronized void watch(){
            if(flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("观众观看:"+this.voice);
            flag=!flag;
    //        通知演员生产
            this.notifyAll();
    
        }
    
    }

    线程池的使用

    package 多线程使用.线程协作;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    //测试线程池
    public class Testpool {
        public static void main(String[] args) {
    //        创建服务,创建线程池
    //        参数为线程池大小
            ExecutorService pool = Executors.newFixedThreadPool(10);
    
            pool.execute(new MyThread());
            pool.execute(new MyThread());
            pool.execute(new MyThread());
            pool.execute(new MyThread());
            pool.execute(new MyThread());
    //        关闭连接
            pool.shutdown();
        }
    }
    class MyThread implements Runnable{
    
        @Override
        public void run() {
                System.out.println(Thread.currentThread().getName());
    
        }
    }

    多线程总结

    多线程的创建

    • 继承Thread类
    • 实现Runnable接口
    • 实现Callable接口
  • 相关阅读:
    python之----------字符编码具体原理
    css部分复习整理
    秘钥登录服务器执行shell脚本
    idea配置github
    InteliJ IDEA 简单使用:配置项目所需jdk
    IntelliJ IDEA 中安装junit插件
    IDEA 运行maven命令时报错: -Dmaven.multiModuleProjectDirectory system propery is not set
    idea如何编译maven项目
    idea项目左边栏只能看到文件看不到项目结构
    idea如何导入一个maven项目
  • 原文地址:https://www.cnblogs.com/ljjdyz/p/15400944.html
Copyright © 2011-2022 走看看