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
        //...


    }
}

 

posted @ 2019-10-11 08:03  不咬人的兔子  阅读(118)  评论(0编辑  收藏  举报