zoukankan      html  css  js  c++  java
  • java

    线程基础

    https://www.cnblogs.com/clamp7724/p/11648308.html

    多线程实现

    https://www.cnblogs.com/clamp7724/p/11649719.html

    join:

    join的底层源码:

    while (isAlive()) 用这个判断join进来的线程是否还在执行

    当A join( x )到 B中时,

    B等待A x秒,然后停止等待。两个线程继续并行

    如果join中没参数,则B一直等待直到A不再执行。

    用这个方法可以控制A,B执行的顺序。

    package joinTest;
    
    public class JoinTest {
        public static void main(String[] args){
            FirstThread t1 = new FirstThread();
            SecondThread t2 = new SecondThread();
    
            //我们无法直接控制线程的执行顺序,
            // 有时候我们希望控制线程的顺序只能通过一些技巧和调用一些方法。
    //        t1.start();
    //        t2.start();
            //正常情况下,虽然t1.start在t2前面,但是不一定谁先执行谁先结束。有时候我们希望t2必须比t1先结束,这时候可以用join,
            // 把t2线程加到t1中,变成t1的一部分,这样t2必定比t1先结束。
            //join:把两个线程合并成一个,运行到join时先把join的线程执行完再继续执行被jion的线程。
            t1.start();
            //第一个线程开始
            //第二个线程开始
            //第二个线程结束
            //第一个线程结束
    
    
        }
    }
    package joinTest;
    
    public class FirstThread extends Thread{
    
            @Override  //注解,表示这个方法是重写的。可以理解为给计算机看的注释。
            public void run() {
                System.out.println("第一个线程开始");
                SecondThread t2 = new SecondThread();
                t2.start();
                try {
                    t2.join();
    
    
                    ////join中可以添加参数
                    //t2.join(2000);
                    //表示t2添加入t1中,给2秒的执行时间,如果没有执行完毕,则继续t1和t2并行,因为t2中等待了5秒,所以执行结果为
                    //第一个线程开始
                    //第二个线程开始
                    //第一个线程结束
                    //第二个线程结束
    
                    //测试:t2.join(4999);
                    //发现结果有时第一个先结束,有时第二个,说明t2结束后是两者继续一起并行,并不是切换。
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第一个线程结束");
            }
    }
    package joinTest;
    
    public class SecondThread extends Thread{
    
        @Override
        public void run() {
            System.out.println("第二个线程开始");
            try {
                Thread.sleep(5000);  //sleep是静态方法,固定Thread.sleep()就好
                //让第二个线程执行的时候等5秒,这样可以体现出一个执行的过程,如果两个线程没有先后关系,FirstThread必然先执行完。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第二个线程结束");
        }
    }

    join:

    A在准备把B踢出自身线程的时候需要操作B,所以需要访问B。如果B被访问时处于被锁状态则A无法踢出B只能继续等待。

    修改SecondThread,加入ThirdThread

    package joinTest;
    
    public class SecondThread extends Thread{
    
        @Override
        public void run() {
            System.out.println("第二个线程开始");
            ThirdThread t3 = new ThirdThread(this);//把当前线程二传进去,让线程三锁定当前线程二
            t3.start();//t3开始运行,这样保证了t1先开始,然后t2,然后t3
            //第一个线程开始
            //第二个线程开始
            //第三个线程开始
            //锁定线程二
            //-----5秒(t2执行了5秒,t3也并行了5秒)
            //第二个线程结束
            //-----5秒(t3总共执行了10秒结束)
            //释放线程二
            //第三个线程结束
            //-----线程二被释放,所以线程一得以继续进行
            //第一个线程结束
            //因为t3没有join进t2,所以并不影响t2运行,只是让t1无法踢出t2只能一直等着t2结束,所以结束顺序是t2,t3,t1.
    
    //        try {
    //            t3.join();
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }
            //如果加了join,则t2会等到t3结束(等10秒)才继续进行(再等5秒,然后t1才能继续),可以看出t3 jion t2, t2 join t1 后, t1的等待时间是t2和t3运行时间之和,3个线程合并成1条。
            //第一个线程开始
            //第二个线程开始
            //第三个线程开始
            //锁定线程二
            //-----10秒
            //释放线程二
            //第三个线程结束
            //-----5秒
            //第二个线程结束
            //第一个线程结束
    
    
            try {
                Thread.sleep(5000);  //sleep是静态方法,固定Thread.sleep()就好
                //让第二个线程执行的时候等5秒,这样可以体现出一个执行的过程,如果两个线程没有先后关系,FirstThread必然先执行完。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第二个线程结束");
        }
    }
    package joinTest;
    
    import com.sun.source.tree.SynchronizedTree;
    
    public class ThirdThread extends Thread{
    
        private SecondThread t2;
        public ThirdThread(SecondThread t2){
            this.t2 = t2;//把t2对象作为参数传进来,这样可以保证线程三操作的和线程一的线程二是同一个线程对象。
        }
    
        @Override
        public void run() {
            System.out.println("第三个线程开始");
            try {
                synchronized(t2){//让线程三执行时锁定线程二
                    System.out.println("锁定线程二");
                    Thread.sleep(10000);//锁定线程二10秒,看看等待线程二2秒后线程一是否还会继续执行
                    System.out.println("释放线程二");
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println("第三个线程结束");
        }
    }

    上面可以知道,t3锁住t2后,t1需要等t3释放了t2才能踢出t2继续运行。

    如果t1执行时锁住了t3,t2执行时锁住了t1,t3执行时锁住了t2,那么三个人都要等到对方释放才能继续进行,形成“死锁”。

    哲学家吃饭:

    4个哲学家有4根筷子,每个人吃饭先拿左手筷子,再拿右手筷子,就可能形成死锁

    死锁的出现条件:

    1.有共用资源

    2.执行条件相互牵制

    所以解决死锁的办法:

    1.防止共有资源 (尽量。。。因为很多时候无法避免。。。)

    2.用sleep等错开执行时间,防止相互牵制。(比如让哲学家们,按顺序执行,设置sleep时间把他们错开。或者用jion安排好执行顺序。)

    timer:  可以理解为定时器

    package timer;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class TimerTest {
    
        private static int num = 0;//为了记录匿名内部类的执行次数,匿名内部类为了防止数据不一致,只能加入static或者final的外部变量。
    
        public static void main(String[] args){
            ArrayList<String> a = new ArrayList<String>();
            a.add("a");
            a.add("b");
            a.add("c");
            a.add("d");
    
            Date d = new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //设置时间格式
            try {
                d = simpleDateFormat.parse("2019-10-11 09:30:00");//给Date d 赋值,用来作为程序开始执行的时间,其实是只要过了这个时间就会开始,比如你设成当前时间之前10分钟,它会直接启动
            } catch (ParseException e) {
                e.printStackTrace();
            }
    
            Timer t = new Timer();
    
    
            //public void schedule(TimerTask task, Date firstTime, long period)   3个参数:
            //1
            //一个TimerTask类型的abstract类,需要重写run方法。这里用的匿名内部类,也可以写个class继承TimerTask然后作为参数传进来。
            //看不懂的话参考内部类笔记:https://www.cnblogs.com/clamp7724/p/11609138.html
            //2
            //一个Data类型参数,控制开始执行的时间
            //3
            //一个Long类型的参数,表示每过多少毫秒执行一次。
            System.out.println("定时器启动!");
            System.out.println("当前时间:" + simpleDateFormat.format(new Date()));
            t.schedule(
                    new TimerTask() {
                        @Override
                        public void run() {
                            System.out.println("第" + num++ + "次执行");
                            System.out.println(simpleDateFormat.format(new Date()));//显示执行的时间
                        }
            }, d, 3000);
    
            //上面的这局表示,从d 时间开始每过3秒执行一次 run 内部的代码。
            //定时器启动!
            //当前时间:2019-10-11 09:29:22
            //第0次执行
            //2019-10-11 09:30:00
            //第1次执行
            //2019-10-11 09:30:03
            //第2次执行
            //2019-10-11 09:30:06
            //第3次执行
            //2019-10-11 09:30:09
            //...
    
    
        }
    }
  • 相关阅读:
    【转自己的落谷博客】联通块的dfs
    STL中map的简单使用&常数优化
    转自自己的关于落谷计数器【p1239】的题解
    计算机网络三种模型
    docker的常用的一些配置
    容器整体性理解
    如何在类中定义装饰器?
    如何实现属性可修改的函数装饰器?
    如何定义带参数装饰器?
    如何为被装饰的函数保存元数据?
  • 原文地址:https://www.cnblogs.com/clamp7724/p/11651797.html
Copyright © 2011-2022 走看看