Java学习--线程

Java学习阶段二

1. 线程

1.1 创建线程

创建一个线程

  • 法一:继承Thread类
  • 法二:实现Runnable 接口
1.1.1 法一 (继承Thread类):
/*
* 在执行下面代码的过程中,可以在终端输入jconsole 连接当前进程,选择线程进行查看
* 可以发现当main线程结束了而Thread-01线程(即Cat创建的线程)还在继续,说明 --> 不是main线程结束了整个程序就结束了 √
*/

package Thread_;

import static java.lang.Thread.sleep;

public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        //因为Cat类实现了Runnable接口,所以创建cat对象,即创建了一个线程
        Cat cat = new Cat();
        //这里为什么调用start 就会让下面的run方法运行
        //为什么不直接调用run --> cat.run();
        cat.start();
        
        /*
         * 因为run() 就是个普通的方法,如果直接cat.run(),则会直接调用Cat类的run(),而且输出的当前线程名仍然是main,而没有新开一个线程,该程序就变成了一个串行执行的程序
         * 可以理解为,run只是调用者的一个方法,而与创建线程,是否放入线程组里无关
         * 真正实现多线程的是 start() 中的 start0() 方法,而该方法是native方法,由JVM调用,是c/c++实现
         */
        
        

        //主线程是main不会受到cat线程的影响,即使它阻塞,主线程也能继续
        //主线程和子线程会交替执行
        for (int i = 0; i < 30; i++) {
            System.out.println("This is main Thread,"+"Thread name="+Thread.currentThread().getName());
            sleep(1000);
        }
    }
}
class Cat extends Thread{
    int count = 0;
    //这里run方法实际上是实现了Runnable接口
    @Override
    public void run() {
        while (count<60){
            count++;
            System.out.println("This is No."+count+" cat Thread"+" ,Thread name="+Thread.currentThread().getName());
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

image-20220412101423901

1.1.2 法二 (Runnable 接口):
//由于Java是单继承的,在某些情况下该类已经继承了某个父类,无法继承Thread 则法一:继承Thread就不可能了,所以可以实现Runnable接口来实现创建线程
package Thread_;

import static java.lang.Thread.sleep;

public class Thread02 {
    public static void main(String[] args) {
        Dog dog = new Dog();
//        dog.start();   //实现Runnable,而Runnable并没有start方法,故无法实现多线程

        /**
         * 这里使用了设计模式:代理模式
         */
        Thread thread = new Thread(dog);
        thread.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("This is main thread,No."+i+" thread,name="+Thread.currentThread().getName());
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Dog implements Runnable{
    int count = 0;
    @Override
    public void run() {
        while (count<20){
            System.out.println("This is dog thread,thread"+ ++count + ",name="+Thread.currentThread().getName());
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

法二中需要 静态代理模式 进行实现

代理模式的模拟,进而更好理解代理模式

package Thread_;

/**
 * 模拟 代理模式的实现
 * 让一个类A代理实现类B没有的功能x,但在实际实现那个功能x时,该功能x内的实现体y,已经被类B实现了
 * 根据动态绑定机制,实际上还是调用类B的实现体y
 */
public class Simulation {
    public static void main(String[] args) {
        SmallThread smallThread = new SmallThread();
        ThreadProxy threadProxy = new ThreadProxy(smallThread);
        threadProxy.start();//output 这内部实现了多线程的相关功能,这里只是模拟,并未实现其中多线程的功能
    }
}

/**
 * 这里 ThreadProxy 实际上模拟了 Thread,Thread是实现了Runnable接口
 * 通过 这种方式简单模拟了---代理模式
 * 通过ThreadProxy代理,去实现SmallThread这个没有start方法的类(该类实现了Runnable,但Runnable没有start()方法,也就没有start0()这个真正实现多线程的方法) 
 */
class ThreadProxy implements Thread{
    private Runnable target = null;

    public ThreadProxy(Runnable target) {
        this.target = target;
    }

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}


class SmallThread implements Runnable{

    public void start0(){
        System.out.println("这内部实现了多线程的相关功能,这里只是模拟,并未实现其中多线程的功能");
    }

    public void start() {
        start0();
    }

    @Override
    public void run() {
        start();
    }
}
1.1.3 继承Thread 与 实现Runnable接口 对比

image-20220412114433889

售票问题(为解决互斥资源问题)

package Thread_;

import static java.lang.Thread.sleep;

public class SellTicket {
    public static void main(String[] args) {
        Sell st = new Sell();
        Thread thread = new Thread(st);
        Thread thread2 = new Thread(st);
        Thread thread3 = new Thread(st);

        //这里出现互斥资源的问题
        thread.start();
        thread2.start();
        thread3.start();
    }
}
class Sell implements Runnable{
    private static int ticketNum = 20;
    @Override
    public void run() {
        while (ticketNum>0){
            System.out.println(Thread.currentThread().getName() + " 售出一张票,余票:"+ --ticketNum);
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
1.1.4 线程终止
  • 自动终止:线程执行完毕,自动终止
  • 通知终止:线程接到通知,然后终止
/*通知终止*/

package Thread_;

import static java.lang.Thread.sleep;

public class MessageThread {
    public static void main(String[] args) {
        Message message = new Message();
        message.start();

        try {
            sleep(1000*10);     //main线程休眠10秒
            Message.setLoop();      //main 线程发送通知 Thread-0线程即可停止
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Message extends Thread {
    private static boolean loop = true;
    private int count = 0;

    @Override
    public void run() {
        //这里循环是无限的,除非接到 通知 --即另一个线程对其设置loop为false,才能终止该线程
        while (loop) {
            System.out.println(++count + Thread.currentThread().getName() + " ...");
            try {
                sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void setLoop() {
        loop = false;
    }
}
1.1.5 线程常用方法

image-20220412142248162

//优先级: 1  -- MIN PRIORITY
		 5  -- NORMAL PRIORITY
         10 -- MAX PORORITY    
package Thread_;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Methods {
    public static void main(String[] args) {
        Thread workerThread = new Thread(new Worker());
        workerThread.setName("worker");
        workerThread.start();

        try {
            Thread.sleep(5*1000);   //main休眠 5s
            workerThread.setPriority(Thread.MIN_PRIORITY);  //将该线程优先级设为最低
            workerThread.interrupt();     //main 在休眠 5s 后就要中断 workerThread的休眠,而worker的休眠本应该有100s
            System.out.println("worker priority is "+ workerThread.getPriority());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

class Worker implements Runnable {
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " Sleeping...");
            startTime = LocalDateTime.now();
            Thread.sleep(100*1000); //休眠100s
        } catch (InterruptedException e) {
            System.out.println("Get up and begin to work !");
            endTime = LocalDateTime.now();
            System.out.println("slept from "+dateFormat.format(startTime) + " to "+dateFormat.format(endTime));
        }
    }
}

/*执行结果

worker Sleeping...				//这句执行5s后才有后面的
Get up and begin to work !
worker priority is 1
slept from 2022-04-12 14:56:22 to 2022-04-12 14:56:27	//事实也证明worker只休眠了5s

进程已结束,退出代码0
*/

image-20220412145907316

/* join 和 yield */

package Thread_;

import static java.lang.Thread.sleep;

public class JoinThread {
    public static void main(String[] args) {
        B b = new B();
        Thread thread = new Thread(b);
        Thread thread1 = new Thread(new A(thread));
        thread1.start();
        thread.start();
    }
}
class A implements Runnable{

    private final Thread temp;
    private int count = 0;

    public A(Thread temp) {
        this.temp = temp;
    }

    @Override
    public void run() {
        while (count<10){
            try {
                System.out.println("This is "+Thread.currentThread().getName()+" start");
                count++;

                //join : 表示暂时让出cpu,让cpu处理另一个线程,等处理完那个线程,再回到这个位置继续处理后面的代码
                temp.join();
                
                //yield,礼让  不一定能成功
//                Thread.yield();
                
                System.out.println("This is "+Thread.currentThread().getName()+" end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }
}
class B implements Runnable{
    private int count = 0;

    @Override
    public void run() {
        while (count<5)
        {
            System.out.println("This is "+Thread.currentThread().getName() + "------------ start");
            count++;
            System.out.println("This is "+Thread.currentThread().getName() + "------------ end");
        }
    }
}

/* output

This is Thread-0------------ start		
This is Thread-1 start				//这里调用了join
This is Thread-0------------ end
This is Thread-0------------ start
This is Thread-0------------ end
This is Thread-0------------ start
This is Thread-0------------ end
This is Thread-0------------ start
This is Thread-0------------ end
This is Thread-0------------ start
This is Thread-0------------ end
This is Thread-1 end				//接着上面的join后继续
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end
This is Thread-1 start
This is Thread-1 end

进程已结束,退出代码0

*/
1.1.6 用户线程 和 守护线程

image-20220412173404246

//可以设置守护线程来监控想要管理的线程 等等功能
package Thread_;

public class DaemonThread01 {

    /**
     * 这里守护线程myDaemonThread 是“监控”main线程,并且在无限循环中,但如果main线程结束了,守护线程也随即结束
     * @param args
     */
    public static void main(String[] args) {
        MyDaemonThread myDaemonThread = new MyDaemonThread();

        //设置该线程为守护线程
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();

        int count = 0;
        while (count<10){
            System.out.println("This is main thread -->"+Thread.currentThread().getName());
            count++;
        }

    }
}
class MyDaemonThread extends Thread {
    @Override
    public void run() {
        while (true){
            System.out.println("This is main DaemonThread->"+Thread.currentThread().getName());
        }
    }
}
1.1.7 线程的生命周期

image-20220412192105093

image-20220412192339195

通过 getState() 可以查看部分状态

1.2 线程同步

Synchronized

image-20220412193339137

用同步解决卖票问题**(包括同步方法 和 同步代码块)(静态同步方法 和 静态方法中有同步代码块**

package Thread_;

import static java.lang.Thread.sleep;

public class SellTicket {
    public static void main(String[] args) {
        Sell st = new Sell();
        Thread thread = new Thread(st);
        Thread thread2 = new Thread(st);
        Thread thread3 = new Thread(st);

        //这里使用同步解决问题
        //虽然创建了3个线程,但是操作的都是同一个对象,这与下面同步方法 或是 同步代码块有关
        //同步方法 或是 同步代码块 加的锁,都必须是针对同一个对象
        //如这里:三个线程 针对的 都是一个对象 st
        thread.start();
        thread2.start();
        thread3.start();
    }
}
class Sell implements Runnable{
    private static int ticketNum = 200;

    /**
     * 访问修饰符 synchronized 返回类型 方法名(){}
     * 增加synchronized 使得 sellFunc() 变成同步方法
     * 互斥锁 也是加到对象上的
     * 简而言之,这个对象是要被共享的对象
     */
    public synchronized void sellFunc(){
        //如果没有第一个if判断的话,可能会超卖 卖成负数
        if(ticketNum <= 0){
            return;
        }
        System.out.println(Thread.currentThread().getName() + " 售出一张票,余票:"+ --ticketNum);
        try {
            sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //-----------------------------------------------------------------------------
    public void sellFunc2(){
        /**
         * synchronized(对象)
         * 同步代码块的锁加到 this对象上
         * 这个this 是要被共享的
         */
        synchronized (this) {
            //如果没有第一个if判断的话,可能会超卖 卖成负数
            if (ticketNum <= 0) {
                return;
            }
            System.out.println(Thread.currentThread().getName() + " 售出一张票,余票:" + --ticketNum);
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //-----------------------------------------------------------------------------

    /**
     * 1.静态方法要是有synchronized 代码块,
     * 2.或者是 静态同步方法
     * 所针对的是这个类而不是对象 ( 理解基础:静态方法是在类创建时就会进行创建的 )
     */
    public static void Code(){
        synchronized (Sell.class){
            System.out.println("This is a static func has synchronized codeblocks");
        }
    }
    public synchronized static void Code2(){
        System.out.println("This is a synchronized static func");
    }



    @Override
    public void run() {
        while (ticketNum>0){
//            sellFunc();
            sellFunc2();
        }
    }
}

image-20220412200352935

image-20220412202504839

尽量使用同步代码块

会释放锁的操作:

image-20220412203426248

不会释放锁的操作
image-20220412203618885

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值