day23 Java 线程


I know, i know
地球另一端有你陪我




基本定义

进程
正在运行的程序,是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源

线程
是进程中的单个顺序控制流,或者说就是一个单独执行路径
一个进程如果只有一条执行路径,称之为单线程程序
一个进程如果有多条执行路径,称之为多线程程序
线程是包含在进程中的

并行
指的是逻辑上同时发生,指在某一时间段内同时运行多个程序

并发
指的是物理上同时发生,指在某一时间点上同时运行多个程序

Java程序的运行原理
java 命令会启动 java 虚拟机,启动 JVM,
等于启动了一个应用程序,也就是启动了一个进程
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法

JVM启动时是多线程
至少包括主程序和垃圾回收程序


一、创建线程

1、继承 Thread 类

    步骤:
        1、自定义一个子类MyThread1继承Thread类
        2、自定义类重写Thread类中的 run() 方法
        3、写入需要被线程执行的代码
        4、创建子类对象
        5、启动线程 start () 
public class MyThread1 extends Thread{
    
    

    public MyThread1(){
    
    
    }
    public MyThread1(String name){
    
    
        super(name);
    }
    @Override
    public void run() {
    
    
        for(int i = 0; i < 100; i++){
    
    
            System.out.println("韭菜盒子");
        }
    }
}
    MyThread1 myThread1 = new MyThread1("fgh");
    MyThread1 myThread2 = new MyThread1("xl");

//        myThread1.run();
//        run()的调用仅仅是封装了被线程执行的代码
//		  但是直接调用的话是普通的方法调用

//        start()方法的调用,首先单独启动了一个线程
//		  然后再由JVM去调用该线程的run()方法

//        模拟多线程
    myThread1.start();
    myThread2.start();


1.1 Thread 匿名内部类

    public static void main(String[] args) {
    
    
        new Thread(){
    
    
            @Override
            public void run() {
    
    
                for(int i = 1; i <= 100; i++){
    
    
                    System.out.println(getName()+"---"+i);
                }
            }
        }.start();

2、实现 Runnable 接口

    步骤:
        1、创建自定义类实现 Runnable 接口
        2、重写 run() 方法
        3、创建自定义类的对象
        4、创建Thread类的对象,将第三步创建的自定义类对象作为参数传递到构造方法中
public class MyRunnable1 implements Runnable {
    
    
    @Override
    public void run() {
    
    
        for(int i = 0; i < 100; i++){
    
    
            //	由于实现的Runnable接口中并没有getName()方法,
            //	所以这里无法使用Thread类中的getName()方法
            //	间接调用,因为currentThread()是静态的,
            //	可以直接通过类名调用获取当前正在
            //	执行run方法的线程对象(Thread类型),所以可以调用getName()
            System.out.println(Thread.currentThread().getName()+"韭菜盒子");
        }
    }
}
	//	此处只需要创建一个 Runnable 对象即可
    MyRunnable1 myRunnable1 = new MyRunnable1();
	//	利用该对象创建两个线程
    Thread thread1 = new Thread(myRunnable1);
    Thread thread2 = new Thread(myRunnable1);
    
    thread1.setName("小六");
    thread2.setName("fgh");
    
    thread1.start();
    thread2.start();

2.1 Runnable 匿名内部类

    public static void main(String[] args) {
    
    
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i = 1; i <= 100; i ++){
    
    
                    System.out.println(Thread.currentThread()
                    .getName()+"---"+i);
                }
            }
        }).start();
    }

3、实现 Callable 接口,利用线程池

    步骤:
        1、创建自定义类实现 Callable 接口
        2、重写 call() 方法
        3、创建线程池对象
        4、创建自定义类对象
        5、通过 submit() 方法,创建自定义类对象线程
public class Callable1 implements Callable {
    
    
    @Override
    public Object call() throws Exception {
    
    
        for(int i = 1; i <= 100; i++){
    
    
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
        return null;
    }
}
    ExecutorService ex = Executors.newFixedThreadPool(2);
    ex.submit(new Callable1());
    ex.submit(new Callable1());
    ex.shutdown();

二、常用方法

1、赋名
可以利用构造方法赋名,一可利用以下方法

public final void setName(String name)
    MyThread1 myThread1 = new MyThread1();
    myThread1.setName("fgh");

2、获取名字

public final String getName()
	String name = myThread1.getName();

3、休眠
      冷却

public static void sleep(long millis)
	Thread.sleep(500);
	myThread1.sleep(1000);

4、优先级

    多线程中是按照抢占式执行的,其中一个很重要的参照即优先级。范围 [1,10]
     注:优先级高的线程未必绝对优先,参考抽卡的欧洲人

4.1 获取线程优先级

public final int getPriority()
    int priority = myThread1.getPriority();
    System.out.println(priority);
输出结果:5

默认优先级为 5

4.2 设置线程优先级

public final void setPriority(int newPriority)
    myThread1.setPriority(10);

线程范围 [1,10]

5、比翼双飞
      让线程运行一定程度均匀

public static void yield()
	Thread.yield();
	myThread1.yield();

6、中断
6.1 stop

public final void stop()

中断线程,安全问题已弃用

6.2 interrupt

public void interrupt()

设置中断标记,并不真正中断线程

7、等待死亡

public final void join()

其他线程等待这个线程死亡,即优先运行

    myThread1.start();
    myThread1.join();
    myThread2.start();

8、后台线程:(守护线程)
Java中有两类线程:用户线程、守护线程

    用户线程:我们在学习多线程之前所有程序代码,运行起来都是一个个的用户线程

    守护线程:所谓的守护线程,指的就是程序运行的时候在后台提供了一种通用服务的线程
    如垃圾回收线程,它就是一个称职的守护者,并且这种线程并不属于程序不可或缺的部分
    
    非守护线程结束的时候,程序也就终止了,同时会杀死进程种所有的守护线程
    当运行的唯一线程都是守护进程线程时,Java虚拟机将退出

设置守护线程

public final void setDaemon(boolean on)

将线程设置为守护线程这一步骤,必须在启动前设置

    myThread1.setDaemon(true);
    myThread1.start();
    myThread2.start();

三、线程组

把多个线程组合到一起
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,
Java允许程序直接对线程组进行控制的

    Runnable1 runnable1 = new Runnable1();

    Thread thread1 = new Thread(runnable1,"小六");
    Thread thread2 = new Thread(runnable1,"fgh");

    //  默认情况下,所有的线程都属于主线程组。
    //  public final ThreadGroup getThreadGroup()
    ThreadGroup threadGroup1 = thread1.getThreadGroup();
    ThreadGroup threadGroup2 = thread2.getThreadGroup();

    //  public final String getName()返回此线程组的名称
    System.out.println(threadGroup1.getName());
    System.out.println(threadGroup2.getName());

    //  main方法同样属于主线程组
    System.out.println(Thread.currentThread().getThreadGroup().getName());

    //  Thread(ThreadGroup group, Runnable target, String name)
    //  创建新线程加入线程分组
    //  先创建一个新的线程组
    //  ThreadGroup(String name)
    ThreadGroup tg = new ThreadGroup("new group");

    Thread thread3 = new Thread(tg,runnable1,"小六");
    Thread thread4 = new Thread(tg,runnable1,"fgh");

    System.out.println(thread3.getThreadGroup().getName());
    System.out.println(thread4.getThreadGroup().getName());

    //  直接通过组名设置一个组都是守护线程组,组里面的线程都是守护线程
    tg.setDaemon(true);

四、线程池

线程池里的每一个线程代码结束后,并不会死亡,
而是再次回到线程池中成为空闲状态,等待下一个对象来使用

创建线程池对象,Executors 工厂类下的静态方法
newFixedThreadPool 是其中一种线程池

public static ExecutorService newFixedThreadPool(int nThreads)

    //  ExecutorService pool = Executors.newFixedThreadPool(2);
    //  创建一个线程池对象
    ExecutorService ex = Executors.newFixedThreadPool(2);

    //  Future<?> submit(Runnable task)
    //  提交一个可运行的任务执行,并返回一个表示该任务的未来
    Runnable1 runnable1 = new Runnable1();
    //  添加两个线程
    ex.submit(runnable1);
    ex.submit(runnable1);

    //  结束线程池
    ex.shutdown();

    //  线程池已经被关闭,不能再重复提交运行。
    //  ex.submit(myRunnable2);

五、死锁 dielock

两个或者两个以上的线程在争夺资源的过程中,出现了相互等待的现象,叫死锁现象

public class Dielock1 extends Thread {
    
    
    private boolean flag;
    public Dielock1(boolean flag) {
    
    
        this.flag = flag;
    }
    //  创建两个普通类对象
    public static final Object lock1 = new Object();
    public static final Object lock2 = new Object();
    @Override
    public void run() {
    
    
        if (flag) {
    
    
            synchronized (lock1) {
    
    
                System.out.println("1-1锁");

            synchronized (lock2) {
    
    
                System.out.println("1-2锁");
            }}
        } else {
    
    
            synchronized (lock2) {
    
    
                System.out.println("2-1锁");

            synchronized (lock1) {
    
    
                System.out.println("2-2锁");
            }}
        }
    }
}
    Dielock1 dielock1 = new Dielock1(true);
    Dielock1 dielock2 = new Dielock1(false);

    dielock1.start();
    dielock2.start();
运行结果:	1-1锁
            2-1锁

synchronized 就像是劫持了一位对象作为人质
在该同步方法运行完之前,所劫持的人质(对象)不能被释放(其他代码调用)


六、定时器

import java.util.Timer;
import java.util.TimerTask;

/*
        定时器是一个应用十分广泛的线程工具,
        可用于调度多个定时任务以后台线程的方式执行。
        在Java中,可以通过Timer和TimerTask类来实现定义调度的功能


        Timer() 创建一个新的计时器。
        void schedule(TimerTask task, long delay) 
        在指定的延迟之后安排指定的任务执行。
        void schedule(TimerTask task, long delay, long period)
            在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。
        void cancel() 终止此计时器,丢弃任何当前计划的任务。

        TimerTask :任务,抽象类,需要继承 run() 方法
 */
public class TimerDemo {
    
    
    public static void main(String[] args) {
    
    
        //创建定时器对象
        Timer timer = new Timer();

        //调度任务执行
        //void schedule(TimerTask task, long delay) 
        在指定的延迟之后安排指定的任务执行。
//        timer.schedule(new MyTask(),3);
//        timer.cancel();
        timer.schedule(new MyTask(timer),3000);


    }
}

class MyTask extends TimerTask{
    
    

    private Timer timer;

    public MyTask() {
    
    
    }

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

    @Override
    public void run() {
    
    
        System.out.println("bong!!");
        timer.cancel();
    }
}
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

/*
            void schedule(TimerTask task, long delay, long period)
            在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。
 */
public class TimerDemo2 {
    
    
    public static void main(String[] args) {
    
    
        //创建定时器对象
        Timer timer = new Timer();
        //3秒钟执行,每个两秒钟一次
        timer.schedule(new MyTask2(),3000,2000);

    }
}

class MyTask2 extends TimerTask{
    
    

    @Override
    public void run() {
    
    
        try {
    
    
            FileReader fileReader = new FileReader("a3.txt");
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String s = bufferedReader.readLine();
            System.out.println(s);

        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}






总结

多线程的实现方式:
1、继承Thread类,重写run()方法,调用start()方法启动

2、实现Runnable接口,重写run()方法,创建Thread对象
      把Runnable对象当作参数传递,调用start()方法启动

3、实现Callable接口,重写call()方法,提交到线程池中运行,需要结合线程池

猜你喜欢

转载自blog.csdn.net/qq_41464008/article/details/120911404