首页 > 编程语言 >JavaDay6

JavaDay6

时间:2025-01-16 20:21:59浏览次数:1  
标签:JavaDay6 Thread void 线程 student new public

JavaDay6

多线程

单线程:一个程序能够使用一条执行路径,从开始到结束。

而多线程则为——一个进程有多条执行路径

多线程的创建

1、自己造一个类,继承Thread类,重写run()方法,创建该线程类的对象【线程对象】,启动start()

/*
    1、自己造一个类,继承Thread类,重写run方法,创建该线程类的对象【线程对象】,启动【启动线程】。
    
 */
class MyThread1 extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=200;i++){
            System.out.println(i);
        }
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1(); //创建一个线程对象
        MyThread1 t2 = new MyThread1(); //创建一个线程对象
        MyThread1 t3 = new MyThread1(); //创建一个线程对象

//        t1.run();
//        t2.run();
//        t3.run();

        t1.start();
        t2.start();
        t3.start();

    }
}

2、自己造一个类,实现Runnable接口,实现run()方法,创建该线程类对象【线程对象】,启动start()

/*
    2、自己造一个类,实现Runnable接口,实现run方法,创建该线程类的对象,借助Thread类构造方法,创建一个线程对象,启动【启动线程】
 */
class MyRunnable1 implements Runnable{
    @Override
    public void run() {
        for(int i=1;i<=200;i++){
            //Thread.currentThread() -- 当前是哪一个线程在执行run方法的代码逻辑
            System.out.println(Thread.currentThread().getName() + " - "+i);
        }
    }
}

public class MyRunnableDemo1 {
    public static void main(String[] args) {
        MyRunnable1 myRunnable1 = new MyRunnable1();

        //Thread类构造方法
        //Thread(Runnable target)
        //分配一个新的 Thread对象。
//        Thread t1 = new Thread(myRunnable1);
//        Thread t2 = new Thread(myRunnable1);
//        Thread t3 = new Thread(myRunnable1);

        //Thread(Runnable target, String name)  传入Runnable对象的同时给线程起一个名字
        //分配一个新的 Thread对象。
        Thread t1 = new Thread(myRunnable1,"张成阳");
        Thread t2 = new Thread(myRunnable1,"方直");
        Thread t3 = new Thread(myRunnable1,"杨浩东");

        t1.start();
        t2.start();
        t3.start();
    }
}
/*
	注意:
        1、为什么要重写run方法?
		一个线程要执行的功能,这个run方法就是每个线程对象启动时要执行的代码逻辑
        2、启动线程千万不要直接调用run方法,因为这和普通的对象调用run方法没有任何区别。将来我们将一个线程对象启动后,由系统自动分配资源,创建线程,底层调用对应线程中的run方法代码逻辑
        3、一个进程中多个线程执行之间,具有随机性,抢占式调度,线程启动的瞬间并不是立刻就执行,而是具备了CPU执行的资格将来当抢到CPU执行权的时候,才会执行。
        4、线程对象调用start()方法的时候,才算启动,系统创建了一个线程对象,具备了CPU执行的资格,当抢到CPU执行权的时候
            由该线程内部自动调用起对应的run方法代码逻辑,直到未来某一刻某个线程对象中的run方法代码逻辑执行完那么这个线程也就死亡了,变成垃圾,等待被回收。

*/

线程控制

休眠线程sleep()

中断线程stop()|interrupt()

中断后,后续代码继续运行

class MyThread2 extends Thread{
    @Override
    public void run() {
        System.out.println(getName()+" 睡觉了....");
        try {
            Thread.sleep(10000);  // 10s  阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getName()+" 睡醒了....");
    }
}

public class ThreadSleepDemo1 {
    public static void main(String[] args) {
        MyThread3 t1 = new MyThread3();
        t1.setName("张成阳");

        t1.start();

        try {
            Thread.sleep(5000);
//            t1.stop(); // 终止t1线程的执行
            t1.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

加入线程join()

使用join后,其它线程需等待join()线程执行完毕后再抢占执行。

class MyThread3 extends Thread{
    public MyThread4() {
    }

    public MyThread4(String name) {
        super(name);
    }

    @Override
    public void run() {
        for(int i=1;i<=200;i++){
            System.out.println(getName()+" - "+i);
        }
    }
}
public class ThreadJoinDemo1 {
    public static void main(String[] args) {
        MyThread4 t1 = new MyThread4("张成阳");
        MyThread4 t2 = new MyThread4("方直");
        MyThread4 t3 = new MyThread4("杨浩东");

        t1.start();
        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
        t3.start();
    }
}

后台线程(守护线程)

/*
    后台线程(守护线程):
        用户线程
        守护线程

    一个程序如果只有守护线程的话,程序会停止。当用户线程没有了,守护线程就会没有。
 */

class MyThread4 extends Thread{
    public MyThread6() {
    }

    public MyThread6(String name) {
        super(name);
    }

    @Override
    public void run() {
        for(int i=1;i<=200;i++){
            System.out.println(getName()+" - "+i);
        }
    }
}
public class ThreadDaemonDemo1 {
    public static void main(String[] args) {
        MyThread4 t1 = new MyThread4("刘备");
        MyThread4 t2 = new MyThread4("张飞");
        MyThread4 t3 = new MyThread4("关羽");

        t2.setDaemon(true);
        t3.setDaemon(true);

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

线程安全

如何判断一个程序是否有线程安全的问题:

​ a、是否有多线程?

​ b、是否有共享数据【变量】?

​ c、是否有多条语句操作共享数据?

那么我们知道了如何判断程序是否存在线程安全的问题,该如何解决?

1、synchronized关键字

2、lock锁

synchronized关键字

/*
	synchronized关键字使用:
        1、同步代码块,将操作共享数据的代码,使用同步代码块包起来,有点像上锁的概念
            synchronized(全局唯一的对象[锁对象]){
                //操作共享数据的代码
            }

        2、同步方法
        	synchronized(this[锁对象]){
                //操作共享数据的代码
            }

        3、同步静态方法
        	synchronized(当前项目中的.class文件[锁对象]){
                //操作共享数据的代码
            }
        
*/

class MyRunnable4 implements Runnable{
    int tickets = 100;
    Object obj = new Object();

    @Override
    public void run() {
        while (true){
            synchronized (obj){
                if(tickets>0){ // tickets=1
                    try {
                        // w1, w2, w3
                        Thread.sleep(20); // 阻塞
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+" 正在出售第 "+(tickets--)+" 张票。。");
                }
            }
        }
    }
}
public class SellTicketDemo4 {
    public static void main(String[] args) {
        MyRunnable4 myRunnable4 = new MyRunnable4();

        Thread w1 = new Thread(myRunnable4, "窗口1");
        Thread w2 = new Thread(myRunnable4, "窗口2");
        Thread w3 = new Thread(myRunnable4, "窗口3");

        w1.start();
        w2.start();
        w3.start();
    }
}

lock锁

/*
		Lock锁的使用
        普通锁:ReentrantLock
        只读锁:ReentrantReadWriteLock.ReadLock
        只写锁:ReentrantReadWriteLock.WriteLock
*/

class MyRunnable6 implements Runnable {
    int tickets = 100;
    Object obj = new Object();
    ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            // 加锁
            lock.lock();
            if (tickets > 0) { // tickets=1
                try {
                    // w1, w2, w3
                    Thread.sleep(20); // 阻塞
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 正在出售第 " + (tickets--) + " 张票。。");
            }
            // 释放锁
            lock.unlock();
        }
    }
}

public class SellTicketDemo6 {
    public static void main(String[] args) {
        MyRunnable6 myRunnable6 = new MyRunnable6();

        Thread w1 = new Thread(myRunnable6, "窗口1");
        Thread w2 = new Thread(myRunnable6, "窗口2");
        Thread w3 = new Thread(myRunnable6, "窗口3");

        w1.start();
        w2.start();
        w3.start();
    }
}

死锁:即线程之间存在相互等待的现象

等待唤醒机制

该机制建立在线程安全之上

等待唤醒机制中有两个重要角色:【生产者】、【消费者】

【生产者】:生产者在生产数据前需要判断之前生产的数据是否有剩余

【消费者】:消费数据前先查看是否有可供消费的数据,若有则继续消费,若无则通知生产者生产。

//生产者


public class ProductThread extends Thread{
    Student student;

    public ProductThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        int i = 0;
//        Student student = new Student();
        while (true){
            synchronized (student){
                /*
                    对于生产者,在生产数据之前,先看一看数据有没有被消费
                    如果还没有被消费,那么就等待,通知消费者来消费数据
                 */
                if(student.isFlag()){
                    //等待,锁对象调用方法进行等待
                    try {
                        student.wait(); // 阻塞,当锁对象调用唤醒的方法的时候,继续往下运行
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if(i%2==0){
                    student.setName("查镕贤");
                    student.setAge(18);
                }else {
                    student.setName("查镕贤2");
                    student.setAge(20);
                }
                i++;

                student.setFlag(true);
                student.notify(); // 通知消费者消费

            }

        }
    }
}




//消费者


public class ConsumerThread extends Thread{
    Student student;

    public ConsumerThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
//        Student student = new Student();
        while (true){
            synchronized (student){

                /*
                    对于消费者而言,消费数据之前,先看一看数据有没有产生,如果没有产生或上一次产生的数据已经被消费过

                 */
                if(!student.isFlag()){
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(student.getName()+" - "+student.getAge());

                student.setFlag(false);
                student.notify();

            }
        }
    }
}

//程序运行


public class ThreadDemo3 {
    public static void main(String[] args) {
        Student student = new Student();

        //创建生产者线程对象
        ProductThread productThread = new ProductThread(student);
        //创建消费者线程对象
        ConsumerThread consumerThread = new ConsumerThread(student);

        productThread.start();
        consumerThread.start();
    }
}

线程组

ThreadGroup,建立线程组对线程进行管理。Java程序允许对线程组直接管理。

每个线程默认有一个线程组

/*
    ThreadGroup: java提供用于描述线程组的类

    1、每个线程默认有一个线程组 getThreadGroup(), 默认线程组的名字叫做main
    2、为了方便对批量的线程做设置
 */
class MyThread1 extends Thread {
    public MyThread1() {
    }

    public MyThread1(ThreadGroup group, String name) {
        super(group, name);
    }

    @Override
    public void run() {
        for (int i = 1; i <= 200; i++) {
            System.out.println(getName() + " - " + i);
        }
    }
}

public class ThreadGroupDemo1 {
    public static void main(String[] args) {
        //创建3个线程对象
//        MyThread1 t1 = new MyThread1();
//        MyThread1 t2 = new MyThread1();
//        MyThread1 t3 = new MyThread1();

//        System.out.println(t1.getThreadGroup().getName());
//        System.out.println(t2.getThreadGroup().getName());
//        System.out.println(t3.getThreadGroup().getName());

        //需求:创建一个名字叫帅哥组的线程组,再创建几个线程对象放到线程组中
        //ThreadGroup(String name) 构造一个新的线程组。
        ThreadGroup tg1 = new ThreadGroup("帅哥组");
        ThreadGroup tg2 = new ThreadGroup("修仙组");

        //Thread(ThreadGroup group, String name) 分配一个新的 Thread对象。
        //创建线程对象的时候,给线程设置一个线程组
        MyThread1 t1 = new MyThread1(tg1,"查镕贤");
        System.out.println("t1当前的线程名字叫做:"+t1.getName()+", 属于线程组:"+t1.getThreadGroup().getName());
        MyThread1 t2 = new MyThread1(tg1,"查镕贤2");
        System.out.println("t2当前的线程名字叫做:"+t2.getName()+", 属于线程组:"+t2.getThreadGroup().getName());
        MyThread1 t3 = new MyThread1(tg1,"查镕贤3");
        System.out.println("t3当前的线程名字叫做:"+t3.getName()+", 属于线程组:"+t3.getThreadGroup().getName());

        //Thread(ThreadGroup group, String name) 分配一个新的 Thread对象。
        //创建线程对象的时候,给线程设置一个线程组
        MyThread1 t4 = new MyThread1(tg2,"查镕贤4");
        System.out.println("t4当前的线程名字叫做:"+t4.getName()+", 属于线程组:"+t4.getThreadGroup().getName());
        MyThread1 t5 = new MyThread1(tg2,"查镕贤5");
        System.out.println("t5当前的线程名字叫做:"+t5.getName()+", 属于线程组:"+t5.getThreadGroup().getName());
        MyThread1 t6 = new MyThread1(tg2,"查镕贤6");
        System.out.println("t6当前的线程名字叫做:"+t6.getName()+", 属于线程组:"+t6.getThreadGroup().getName());

        // 将修仙组的线程设置为守护线程
//        t4.setDaemon(true);
//        t5.setDaemon(true);
//        t6.setDaemon(true);
        tg2.setDaemon(true);
    }
}

线程池

加快用户的访问速度,提高固定时间内的访问量

线程池需要手动关闭:.shutdown

创建线程除了刚开始说的两种方式外,接下来引入第三种:

实现Callable接口,借助线程池中的submit方法提交,启动。

class MyCallable implements Callable{

    @Override
    public Object call() throws Exception {
        for (int i=1;i<=200;i++){
            System.out.println(Thread.currentThread().getName()+" - "+i);
        }

        return null;
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=1;i<=200;i++){
            System.out.println(Thread.currentThread().getName()+" - "+i);
        }
    }
}


public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //创建一个固定大小的线程池
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //将线程放入到线程池中运行
        //Future<?> submit(Runnable task) 提交一个可运行的任务执行,并返回一个表示该任务的未来。
        pool.submit(new MyRunnable()); //底层是使用一个Thread类将其封装成线程对象,启动 // pool-1-thread-1
        pool.submit(new MyRunnable()); //底层是使用一个Thread类将其封装成线程对象,启动 // pool-1-thread-2
        pool.submit(new MyRunnable()); //底层是使用一个Thread类将其封装成线程对象,启动

        //<T> Future<T> submit(Callable<T> task) 提交值返回任务以执行,并返回代表任务待处理结果的Future。
        pool.submit(new MyCallable());
        pool.submit(new MyCallable());
        pool.submit(new MyCallable());

        //使用匿名内部类,来提交线程到线程池,不同的线程内部可以是不同的实现。
        pool.submit(new Runnable() {
            @Override
            public void run() {
                // 运行代码逻辑1
            }
        });

        pool.submit(new Runnable() {
            @Override
            public void run() {
                // 运行代码逻辑2
            }
        });
        
        //线程池需要手动关闭
        pool.shutdown();
    }
}

注意:

​ Runnable接口和Callable接口的区别:

​ 其中实现Runnable接口和实现Callable接口区别在于,Runnable接口中是实现run方法,run方法没有返回值而Callable接口中是实现call方法,call方法可以有返回值的,将来如果一个线程执行完毕后需要返回结果,那么就使用Callable接口借助线程池的方式实现多线程。

同时,还可使用匿名内部类来提交不同的线程到线程池

定时器

Timer:定时器

TimerTask:定时任务

关系:创建定时任务【TimerTask】由定时器【Timer】进行调度。

/*
    Timer中提供了两种调度方式:
        public void schedule(TimerTask task, long delay) 延迟指定时间后执行
        public void schedule(TimerTask task,long delay,long period) 延迟指定时间后执行,之后每间隔固定时间重复执行

 */
public class TimerDemo {
    public static void main(String[] args) {
        //创建定时器
        //Timer()
        //创建一个新的计时器。
        Timer timer = new Timer();
        //public void schedule(TimerTask task, long delay)
        timer.schedule(new MyTask(timer), 10000L);
        //public void schedule(TimerTask task,long delay,long period) 延迟指定时间后执行,之后每间隔固定时间重复执行
        timer.schedule(new MyTask2(), 10000L, 2000L);
    }
}

//创建一个定时任务类,继承TimerTask抽象类,重写run方法,编写任务代码逻辑
class MyTask2 extends TimerTask{

    Timer timer;

    public MyTask2() {
    }

    public MyTask2(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        for(int i=1;i<=3;i++){
            Thread.sleep();
        }
        System.out.println("砰!!!爆炸了!!!");
        fun1();
        // 关闭定时器
        timer.cancel();
    }


    public void fun1(){

    }
}

//创建一个定时任务类,继承TimerTask抽象类,重写run方法,编写任务代码逻辑
class MyTask extends TimerTask{

    Timer timer;

    public MyTask(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        System.out.println("砰!!!爆炸了!!!");
//        fun1();
        // 关闭定时器
        timer.cancel();
    }

设计模式

历代程序员开发时提供的经验和开发思路

设计模式分类:

创建型模式【重点】

​ 行为型模式

​ 结构型模式

创建型模式

简单工厂模式

a、好处是可通过一个类创建多种对象,不需要在测试类中new

b、不便之处是若对象种类太多,则需要频繁修改类

//自建的Animal类:

public abstract class Animal {
    private String name;
    private int age;

    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();

    public abstract void sleep();
}


//继承自上Animal类创建猫和老虎:

public class Cat extends Animal{
    public Cat() {
    }

    public Cat(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("

标签:JavaDay6,Thread,void,线程,student,new,public
From: https://www.cnblogs.com/Roxan-bd/p/18675704

相关文章