Java研学-多线程

一 名词解析

1 线程 : 控制指定APP(进程)执行的最基本单元(最小单位)

2 进程 : 硬件设备上的每一个应用程序

3 单线程 : 一个进程中只有一个线程执行,实际上基本没有这种情况

4 多线程 : 一个进程中至少有两个或两个以上的线程在执行

二 创建方式

1 共有三种:Thread类. Runnable接口,Callable接口

2 多线程操作:多个线程在同时一时刻操作共同资源

3 继承Thread类,重写run方法(编写多线程操作的代码),通过Thread类或Thread子类对象调用start方法开启多线程,并调用指定的run方法执行多线程操作,每次执行的结果都不一致

①Thread类:java中所有有关线程操作的类

// 子线程
public class SonThread extends Thread{
    public void run(){
        for(int i=0;i<20;i++){
            System.err.println("子线程::"+i);
        }
    }
}

// 测试
public class ThreadTest {
    public static void main(String[] args) {
        SonThread st=new SonThread();
        for (int i = 0; i < 20; i++) {
            System.out.println("main::"+i);
        }
        // 调用重写的run方法,根据调用顺序执行线程操作(单线程按顺序执行)
        //st.run();

        // 实现多线程操作,通过Thread类对象调用start方法实现,run方法执行
        st.start();
        for (int i = 0; i < 20; i++) {
            System.err.println("main::"+i);
        }
    }
}

② 实现Runnable接口,重写run方法,需调用Thread类中的start方法开启多线程,并执行指定run方法,避免java中单继承的局限性,和继承带来的类与类之间的耦合度的增加

// 实现类
public class Son implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.err.println("Son::"+i);
        }
    }
}

// 测试类
public class SonTest {
    public static void main(String[] args) {
        // 实例化实现Runable的对象
        Son s=new Son();
        // 与Thread类建立联系
        Thread t=new Thread(s);
        t.start();
        for (int i = 0; i < 500; i++) {
            System.out.println("main::"+i);
        }
    }
}

③ 实现Callable接口可返回结果并且可能抛出异常的任务。可以获得任务执行返回值;通过与Future的结合,可以实现利用Future来跟踪异步计算的结果。

// 实现callable接口
public class TestCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 50; i++) {
            System.err.println("call::"+i);
        }
        return i;
    }
}

// 使用
public class Play {
    public static void main(String[] args) {
        TestCallable tc = new TestCallable();
        FutureTask<Integer> ft = new FutureTask<>(tc);
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
            if (i == 20) {
                new Thread(ft, "有返回值的线程").start();
            }
        }
        try {
            System.out.println("子线程的返回值:" + ft.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

4 通过上述多线程操作,每次执行后,效果不一致,是因为多个线程同时操作相同资源,每个线程都会去"抢占"CPU的执行权,谁抢到,谁执行.实际上,是CPU在已开启的多个线程之间进行快速的切换,也称之为多线程执行的特点(随机性)

5 可提高指定线程的执行权限,java中为每个线程设置初始默认"等级",为5,等级取值范围[1,10],通过Thread类提供的指定的set方法去设置等级,即优先级;但是并不是优先级越大越先执行,而优先级的大小只是提高被执行完的可能

三 Thread类常见API

1 创建Thread类对象方法

public Thread()

2 将指定的Runnable接口的实现类对象,通过Thread构造器与Thread类建立联系

public Thread(Runnable r)

3 编写多线程操作代码方法

public void run()

4 开启多线程操作,并调用指定的run方法执行多线程

public void start()

5 获取当前线程的名字

public class SonThread extends Thread{
    public void run(){
        for(int i=0;i<50;i++){
            System.err.println(getName()+"::"+i);
        }
    }
}

6 在非Thread类子类中,获取当前正在执行线程对象

public class SonTest {
    public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread()+"::"+i);
        }//Thread[main,5,main]::40
    }//格式为:线程名 优先级 线程的执行位置
}

7 让指定的线程睡眠指定的时间(时间的单位是毫秒数)

// public static void sleep(long millis)
public class SonThread extends Thread{
    public void run(){
        for(int i=0;i<50;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.err.println(getName()+"::"+i);
        }
    }
}

8 给线程设置线程名

public class SonThread extends Thread{
    public void run(){
        setName("我是线程");
        for(int i=0;i<50;i++){
            System.err.println(getName()+"::"+i);
        }
    }
}

// 或者
public class SonTest {
    public static void main(String[] args) {
        Thread.currentThread().setName("我是线程");
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread()+"::"+i);
        }
    }
}

9 设置和获取线程的优先级,优先级的取值范围[1,10]

public int getPriority()

public class SonTest {
    public static void main(String[] args) {
        SonThread st=new SonThread();
        st.start();
        // 设置
        st.setPriority(10);
        for (int i = 0; i < 50; i++) {
            System.err.println(Thread.currentThread()+"::"+i);
        }
        // 获取
        System.out.println(st.getPriority());
    }
}

四 多线程的状态

Java研学-多线程_第1张图片

状态 权限
创建状态 生存权
运行状态 既有生存权又有执行权
消亡状态 既没有生存权也没有执行权
临时(阻塞)状态 只有生存权没有执行权

1 创建状态下的生存权和临时状态的生存权的区别

  在于多线程操作是否被开启;创建状态下,没有进行多线程操作;临时状态下的生存权在开启多线程之后,没有获取到执行权

2 处于临时状态下的线程存储位置是在由jvm虚拟机提供的线程池,进出的原则,先进先出(类似于吸管,也称之为队列式数据存储

3 等待唤醒机制的等待和唤醒方法定义在Object类中,而不是定义在Thread类中

  由于实际开发中,并不是所有的类都要继承Thread类,但是只要是java的类就是多线程操作,等待和唤醒的方法是共性的,需要定义在所有类的父类中,因此就把它定义在了Object类中,实际大家更喜欢用sleep来完成多线程操作:到时候自动苏醒

五 案例-火车站窗口售票

// 测试类
public class Test {
    public static void main(String[] args) {
        Window w=new Window();
        //通过Thread构造器建立连接
        // 多线程操作时,多个线程可以同时操作共同资源,当一个线程操作时,其他的线程也有操作的可能
        Thread w1=new Thread(w);
        Thread w2=new Thread(w);
        Thread w3=new Thread(w);
        w1.start();
        w2.start();
        w3.start();
    }
}

// 同步代码块版Window 
public class Window implements Runnable{
    private int tickets=100;
    @Override
    public void run() {
        // 火车站窗口24小时开放
        while (true){
            // 添加锁,防止超卖问题(3种)
            // Object obj=new Object();
            // this --本类对象锁
            // Window.class --反射机制锁,通过字节码文件获取
            synchronized (Window.class){
               //同步代码块
                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if(tickets > 0){
                    System.err.println(Thread.currentThread().getName()+"::"+tickets--);
                }
            }
        }
    }
}    

// 同步函数版Window 
public class Window implements Runnable{
    private int tickets=100;
    @Override
    public void run() {
        // 火车站窗口24小时开放
        while (true){
            fun();
        }
    }
    // 同步函数
    private synchronized void fun() {
        if(tickets > 0){
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.err.println(Thread.currentThread().getName()+"::"+tickets--);
        }
    }
}

同步函数和同步代码块的区别

  ① 同步代码块有3把锁:Object对象锁 this本类对象锁 反射机制锁

  ② 同步函数有1把锁:this本类对象锁

  ③ 同步函数只能锁定当前对象(即this),而同步代码块可以锁定任意对象。

  ④ 同步函数的锁是在方法调用时自动加上的,而同步代码块需要手动指定锁对象。

  ⑤ 同步函数的锁是在方法调用结束时自动释放的,而同步代码块需要手动释放锁。

你可能感兴趣的:(#,Java研学,java,开发语言)