Java基础知识之线程

/**
 * 实现线程的第一种方式:
 *     编写一个类,直接继承java.lang.Thread,重写run方法。
 *
 *     怎么创建线程对象? new就行了。
 *     怎么启动线程呢? 调用线程对象的start()方法。
 *
 * 注意:
 *     亘古不变的道理:
 *         方法体当中的代码永远都是自上而下的顺序依次逐行执行的。
 *
 * 以下程序的输出结果有这样的特点:
 *     有先有后。
 *     有多有少。
 */
public class ThreadTest01 {
    public static void main(String[] args) {
        // 这里是main方法,这里的代码属于主线程,在主栈中运行。
        // 新建一个分支线程对象
        MyThread t=new MyThread();

        // 启动线程
        //t.run(); // 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
        // start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
        // 这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
        // 启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
        // run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
        t.start();
        // 这里的代码还是运行在主线程中。
        for(int i=0;i<1000;i++){
            System.out.println("主线程--------------"+i);
        }
    }
}

class MyThread extends Thread{
    public void run(){
        // 编写程序,这段程序运行在分支线程中(分支栈)。
        for(int i=0;i<1000;i++){
            System.out.println("分支线程--------------"+i);
        }
    }
}

 

运行结果:

/**
 * 实现线程的第二种方式,编写一个类实现java.lang.Runnable接口。
 */
public class ThreadTest02 {
    public static void main(String[] args) {
        // 创建一个可运行的对象
        MyRunnable r=new MyRunnable();
        // 将可运行的对象封装成一个线程对象
        Thread t=new Thread(r);

        //Thread t=new Thread(new MyRunnable());以上两行代码的合并
        //启动线程
        t.start();

        for(int i=0;i<100;i++){
            System.out.println("主线程----------"+i);
        }
    }
}

// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("分支线程---------"+i);
        }
    }
}

 

代码结果:

/**
 * 采用匿名内部类的方式
 */
public class ThreadTest03 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable(){
            public void run(){
                for(int i=0;i<100;i++){
                    System.out.println("分线程-------"+i);
                }
            }
        });

        t.start();
        for(int i=0;i<100;i++){
            System.out.println("主线程----------"+i);
        }
    }
}

 

/**
 * 1、怎么获取当前线程对象?
 *     Thread t = Thread.currentThread();
 *     返回值t就是当前线程。
 *
 * 2、获取线程对象的名字
 *     String name = 线程对象.getName();
 *
 * 3、修改线程对象的名字
 *     线程对象.setName("线程名字");
 *
 * 4、当线程没有设置名字的时候,默认的名字有什么规律?(了解一下)
 *     Thread-0
 *     Thread-1
 *     Thread-2
 *     Thread-3
 *     .....
 */
public class ThreadTest04 {
    public void dosome(){
        // 这样就不行了
        //this.getName();
        //super.getName();
        //这样可以
        String name=Thread.currentThread().getName();
        System.out.println(name);
    }

    public static void main(String[] args) {
        ThreadTest04 tt=new ThreadTest04();
        tt.dosome();

        //currentThread就是当前线程对象
        // 这个代码出现在main方法当中,所以当前线程就是主线程。
        Thread currentThead=Thread.currentThread();
        System.out.println(currentThead.getName());

        // 创建线程对象
        MyThread2 t=new MyThread2();
        // 设置线程的名字
        t.setName("t1线程");
        // 当没有设置线程的名字默认名字形式:Thread-0开始
        //String tname=t.getName();
        //System.out.println(tname);

        MyThread2 t1=new MyThread2();
        t1.setName("t2线程");
        //System.out.println(t1.getName());

        t.start();
        t1.start();

    }
}

class  MyThread2 extends Thread{
    public void run(){
        for(int i=0;i<10;i++){
            // currentThread就是当前线程对象。当前线程是谁呢?
            // 当t1线程执行run方法,那么这个当前线程就是t1
            // 当t2线程执行run方法,那么这个当前线程就是t2
            Thread currentThread=Thread.currentThread();
            System.out.println(currentThread.getName()+"--------"+i);
            //System.out.println(this.getName()+"=========="+i);
            //System.out.println(super.getName()+"---------"+i);
        }
    }
}

 

执行结果:

/**
 * 关于线程的sleep方法:
 *     static void sleep(long millis)
 *     1、静态方法:Thread.sleep(1000);
 *     2、参数是毫秒
 *     3、作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。
 *         这行代码出现在A线程中,A线程就会进入休眠。
 *         这行代码出现在B线程中,B线程就会进入休眠。
 *     4、Thread.sleep()方法,可以做到这种效果:
 *         间隔特定的时间,去执行一段特定的代码,每隔多久执行一次。
 */
public class ThreadTest05 {
    public static void main(String[] args) {
        try {
            // 让当前线程进入休眠,睡眠5秒
            // 当前线程是主线程!!!
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 5秒之后执行这里的代码
        System.out.println("5秒之后显示");

        for(int i=0;i<10;i++){
            System.out.println("------------"+i);
            try {
                // 每输出一次睡眠1秒在继续输出
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

public class ThreadTest06 {
    public static void main(String[] args) {
        Thread t = new MyThread3();
        t.setName("t");
        t.start();

       /* 调用sleep方法
        问题:下面这行代码会让线程t进入休眠状态吗?
        在执行的时候还是会转换成:
        这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠。
        这样代码出现在main方法中,main线程睡眠,t线程并不会受影响。*/
        try {
            t.sleep(1000 * 5);
            //和下面这行代码效果相同
            //Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("这段代码什么时候执行");

    }
}

class MyThread3 extends Thread{
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
    }
}

 

/**
 * sleep睡眠太久了,如果希望半道上醒来,你应该怎么办?也就是说怎么叫醒一个正在睡眠的线程??
 *     注意:这个不是终断线程的执行,是终止线程的睡眠。
 */
public class ThreadTest07 {
    public static void main(String[] args){
        Thread t=new Thread(new MyRunnable2());
        t.setName("t线程");
        t.start();

        // 希望5秒之后,t线程醒来(5秒之后主线程手里的活儿干完了。)
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
        t.interrupt();
    }
}

class MyRunnable2 implements Runnable{
    // 重点:run()当中的异常不能throws,只能try catch
    // 因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。
    public void run(){
        System.out.println(Thread.currentThread().getName()+"---begin");
        try {
            Thread.sleep(1000*60*60);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //1小时之后才会执行这里
        System.out.println(Thread.currentThread().getName()+"-----end");

    }
}

 

/**
 * 怎么合理的终止一个线程的执行。这种方式是很常用的。
 */
public class ThreadTest08 {
    public static void main(String[] args) {
        MyRunnable4 r=new MyRunnable4();
        Thread t=new Thread(r);
        t.setName("t线程");
        t.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 终止线程
        // 你想要什么时候终止t的执行,那么你把标记修改为false,就结束了。
        r.run=false;
    }
}

class MyRunnable4 implements Runnable{
    // 打一个布尔标记
    boolean run=true;
    public void run(){
        for(int i=0;i<10;i++){
            if(run){
                System.out.println(Thread.currentThread().getName()+"---------"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                System.out.println("进程结束");
                // return就结束了,你在结束之前还有什么没保存的。
                // 在这里可以保存呀。
                //save....

                //终止当前线程
                return;
            }
        }
    }
}

 

/**
 * 关于线程的优先级
 */
public class ThreadTest09 {
    public static void main(String[] args) {
        // 设置主线程的优先级为1
        Thread.currentThread().setPriority(1);

        System.out.println("最高优先级" + Thread.MAX_PRIORITY); //10
        System.out.println("最低优先级" + Thread.MIN_PRIORITY);  //1
        System.out.println("默认优先级" + Thread.NORM_PRIORITY);  //5

        Thread currentThread = Thread.currentThread();
        Thread t = new Thread(new MyRunnable5());
        //设置t线程的优先级为10
        t.setPriority(10);
        t.setName("t线程");
        t.start();

        // 优先级较高的,只是抢到的CPU时间片相对多一些。
        // 大概率方向更偏向于优先级比较高的。
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }


}

class MyRunnable5 implements Runnable{
    public void run(){
        System.out.println(Thread.currentThread().getName()+"默认优先级"+Thread.currentThread().getPriority());
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
    }
}

 

/**
 * 让位,当前线程暂停,回到就绪状态,让给其它线程。
 * 静态方法:Thread.yield();
 */
public class ThreadTest11 {
    public static void main(String[] args) {
        Thread t=new Thread(new MyRunnable6());
        t.setName("t线程");
        t.start();

        for(int i=0;i<10000;i++){
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}


class MyRunnable6 implements Runnable{
    public void run(){
        for(int i=0;i<10000;i++){
            //每100个让位一次。
            if(i%100==0){
                // 当前线程暂停一下,让给主线程。
                Thread.yield();
            }
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}

 

运行结果

public class ThreadTest12 {
    public static void main(String[] args) {
        System.out.println("main begin");

        Thread t=new Thread(new MyRunnable7());
        t.setName("t线程");
        t.start();

        try {
        // t合并到当前线程中,当前线程受阻塞,t线程执行直到结束。
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main end");
    }
}

class MyRunnable7 implements Runnable{
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}

 

运行结果:

public class ThreadTest13 {
    public static void main(String[] args) {
        Thread t=new DataThread();
        t.setName("备份数据线程");
        // 启动线程之前,将线程设置为守护线程
        t.setDaemon(true);
        t.start();

        // 主线程:主线程是用户线程
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"----"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


class DataThread extends Thread{
    public void run(){
        int i=0;
        // 即使是死循环,但由于该线程是守护者,当用户线程结束,守护线程自动终止。
        while(true){
            System.out.println(Thread.currentThread().getName()+"---"+(++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 实现线程的第三种方式:
 *     实现Callable接口
 *     这种方式的优点:可以获取到线程的执行结果。
 *     这种方式的缺点:效率比较低,在获取t线程执行结果的时候,当前线程受阻塞,效率较低。
 */
public class ThreadTest14 {
    public static void main(String[] args) {
        // 第一步:创建一个“未来任务类”对象。
        // 参数非常重要,需要给一个Callable接口实现类对象。
        FutureTask  task=new FutureTask(new Callable(){
            //call()方法相当于run()方法
            public Object call(){
                // 线程执行一个任务,执行之后可能会有一个执行结果
                // 模拟执行
                System.out.println("call method begin");
                try {
                    Thread.sleep(1000*5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("call method end");
                int a=1;
                int b=2;
                return a+b;
            }
        });

        Thread t=new Thread(task);
        System.out.println("main begin");
        t.start();
        Object obj= null;
        try {
            // 这里是main方法,这是在主线程中。
            // 在主线程中,怎么获取t线程的返回结果?
            // get()方法的执行会导致“当前线程阻塞”
            obj = task.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("线程执行结果"+obj);

        // main方法这里的程序要想执行必须等待get()方法的结束
        // 而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
        // 另一个线程执行是需要时间的。
        System.out.println("这段文字什么时候执行");

    }
}

 

执行结果:

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

/**
 * 1、使用wait方法和notify方法实现“生产者和消费者模式”
 *
 * 2、什么是“生产者和消费者模式”?
 *     生产线程负责生产,消费线程负责消费。
 *     生产线程和消费线程要达到均衡。
 *     这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。
 *
 * 3、wait和notify方法不是线程对象的方法,是普通java对象都有的方法。
 *
 * 4、wait方法和notify方法建立在线程同步的基础之上。因为多线程要同时操作一个仓库。有线程安全问题。
 *
 * 5、wait方法作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁。
 *
 * 6、notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。
 *
 * 7、模拟这样一个需求:
 *     仓库我们采用List集合。
 *     List集合中假设只能存储1个元素。
 *     1个元素就表示仓库满了。
 *     如果List集合中元素个数是0,就表示仓库空了。
 *     保证List集合中永远都是最多存储1个元素。
 *
 *     必须做到这种效果:生产1个消费1个。
 */
public class ThreadTest15 {
    public static void main(String[] args) {
        List list=new ArrayList();

        Thread t1=new Thread(new Producer(list));

        Thread t2=new Thread(new Consumer(list));

        t1.setName("生产者线程");
        t2.setName("消费者线程");

        t1.start();
        t2.start();
    }
}

//生产线程
class Producer implements Runnable{
    //仓库
    private List list;

    public Producer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        // 一直生产(使用死循环来模拟一直生产)
        while(true){
            // 给仓库对象list加锁。
            synchronized(list){
                // 大于0,说明仓库中已经有1个元素了。
                if(list.size()>0){
                    try {
                        // 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁。
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 程序能够执行到这里说明仓库是空的,可以生产
                Object obj=new Object();
                list.add(obj);
                System.out.println(Thread.currentThread().getName()+"---"+obj);
                //唤醒消费者进行消费
                list.notifyAll();
            }
        }
    }
}


//消费线程
class Consumer implements Runnable{
    private List list;

    public Consumer(List list) {
        this.list = list;
    }

    public void run(){
        //一直消费
        while(true){
            synchronized(list) {
                if (list.size() == 0) {
                    try {
                        // 仓库已经空了。
                        // 消费者线程等待,释放掉list集合的锁
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 程序能够执行到此处说明仓库中有数据,进行消费。
                Object obj = list.remove(0);
                System.out.println(Thread.currentThread().getName() + "---" + obj);
                //唤醒生产者生产
                list.notifyAll();
            }

        }
    }
}

 

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 使用定时器指定定时任务。
 * 死锁问题
 *  synchronized出现在静态方法上是找类锁。
 */
public class TimerTest {
    public static void main(String[] args) {
        // 创建定时器对象
        Timer timer=new Timer();
        //守护线程的方式
        Timer timer1=new Timer(true);



        try {
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
            Date firstTime=sdf.parse("2021-03-25 19-06-30 111");

            // 指定定时任务
            //timer.schedule(定时任务, 第一次执行时间, 间隔多久执行一次);
            timer.schedule(new LogTimerTask(),firstTime,5000);


            /*timer.schedule(new TimerTask(){
                public void run(){
                    SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
                    String time=sdf.format(new Date());
                    System.out.println(time+":成功完成一次数据备份");
                }
            },firstTime,5000);*/
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }


}

// 编写一个定时任务类
// 假设这是一个记录日志的定时任务
class LogTimerTask extends TimerTask {
    public void run(){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss SSS");
        String time=sdf.format(new Date());
        System.out.println(time+":成功完成一次数据备份");
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值