JAVA线程讲解

线程

线程路程的关系

“线程和进程的关系:每个进程都有相应的线程,在执行程序时,实际上是执行相应的一系列线程。进程是资源分配的最小单位,线程是程序执行的最小单位。”

概述

状态

新建(NEW):新创建了一个线程对象。

可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生

实现方式

在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口 。

线程启动的方式是调用start方法,start方法会调用run方法。

Runnable

Runnable是一个接口,里面就只有run方法。

public interface Runnable {
    public abstract void run();
}

使用案例

public class MyRunnable implements  Runnable{
    @Override
    public void run() {
        try {
            System.out.println("重写了runable");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    
    public static void main(String[] args) {
        Runnable myRunnable0 = new MyRunnable();
        Thread thread0 = new Thread(myRunnable0);
        Thread thread1 = new Thread(myRunnable0);
        //Runnable myRunnable1 = new MyRunnable();
        //Thread thread1 = new Thread(myRunnable1);
        thread0.start();
        //thread1.start();
    }
}

Thread

概念

hread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的

run()方法就可以实现多线程操作了,但是一个类只能继承一个父类。

案例

​
/**
 * @author xing
 * 2021年9月27日下午12:11:11
 * 继承Thread的用法
 */
public class MyThread extends Thread {
        String str;
        
        public void run() {
            this.show();
        }
        
        public void show() {
            while(true) {
                try {
                    Thread.sleep(1000);//当前线程睡一秒
                    System.out.println(str+" :-----------MyThread-----------"); 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
        public static void main(String[] args) {
            MyThread mt0 = new MyThread();
            MyThread mt1 = new MyThread();
            MyThread mt2 = new MyThread();
            mt0.str = "mt0";
            mt0.start();
            mt1.str = "mt1";
            mt1.start();
            mt2.str = "mt2";
            mt2.start();
        }
}
​

注:每new一个thread就是开辟了一个新的线程,start就是启动了线程。main所运行的就是主线程,主线程可以开启子线程,各个线程干自己的事情。

Callable

当我们执行线程需要返回值的时候那么就必须选用实现Callable类的方式,因为目前只有这种方式能返回值。当然这种方式我们也可以不需要获取返回值。

这种方式是通过FutureTask的get()方法(下面代码的第22行)或者get(long timeout, TimeUnit unit)(下面代码的第28行)方法获取返回值。当我们看Callable的接口定义的源码会发现“public interface Callable<V> ” ,我们实现的时候是需要定义返回类型,如下面代码所示。

除此之外我们还需要注意的是:当我们通过FutureTask的get()方法去获取线程的返回值的时候是要等到线程call()内容都执行完毕之后才能获取得到,并且get()方法后面的代码必须等待,说明这一定是同步的,所以我们可以在真正需要线程返回值的时候才通过get()方法去获取,以免被阻塞。当我们通过get(long timeout, TimeUnit unit)方式去获取的时候可以设置超时时间,如果超过所设置的超时时间都没有获取到线程返回的值则会抛出 java.util.concurrent.TimeoutException 异常,当然如果在get(long timeout, TimeUnit unit)之前用get()方式获取了的话就不会抛异常。

实现Callable还有个好处就是可以线程可以抛异常,如果我们需要在线程里抛出异常的话也可以选用这种方式,其他两种方式只能捕获异常信息。

public class ThreadCallableDemo implements Callable<Integer>{
​
    /** 计数变量 */
    private int count = 0;
​
    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
​
        ThreadCallableDemo threadCallableDemo = new ThreadCallableDemo();
​
        //通过FutureTask获取返回值
        FutureTask<Integer> taskA = new FutureTask<>(threadCallableDemo);
​
        //实例化线程
        Thread thread = new Thread(taskA, "threadCallableDemoA");
        System.out.println(String.format("线程状态preStart: %s", thread.getState()));
​
        //启动线程
        thread.start();
        System.out.println(String.format("线程状态afterStart: %s", thread.getState()));
​
        //通过FutureTask的get()方法获取返回值
        int result = taskA.get();
        System.out.println("是否同步测试....");
        System.out.println(String.format("result: %s", result));
        System.out.println(String.format("线程状态afterGetResult1: %s", thread.getState()));
​
        //通过FutureTask的get()方法获取返回值 设置超时时间 单位为ms
        int resultWithTime = taskA.get(100, TimeUnit.MILLISECONDS);
        System.out.println(String.format("resultWithTime: %s", resultWithTime));
        System.out.println(String.format("线程状态afterGetResult2: %s", thread.getState()));
​
    }
​
    /**
     * 实现Callable的call类
     */
    @Override
    public Integer call() throws Exception {
​
        //自增
        count++;
​
        System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s",
                    Thread.currentThread().getName(), Thread.currentThread().getState(), count));
        System.out.println("休眠1000ms....");
        Thread.currentThread().sleep(1000);
        return count;
    }
}

synchronized

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

synchronized修饰方法案例:

窗口售票实例:

源码

package Dome1;
​
public class TicketDome extends Thread{
    private static int sum=100;
    private static Object obj=new Object();
​
    @Override
    public void run() {
        while (true){//同步代码块
            sum.sleep(1000);
            synchronized (obj){
                if (sum>0) {
                    System.out.println(Thread.currentThread().getName()+"售出的票号是"+sum);
                    sum--;
                }else {
                    System.exit(0);
                }
            }
​
        }
    }
    public synchronized void mp(){
​
    }
​
    public static void main(String[] args) {
        TicketDome ticketDome1 = new TicketDome();
        ticketDome1.start();
​
        TicketDome ticketDome2 = new TicketDome();
        ticketDome2.start();
​
        TicketDome ticketDome3 = new TicketDome();
        ticketDome3.start();
​
​
    }
}

结果

不加入重度锁

源码:

    package Dome1;

public class TicketDome extends Thread{
    private static int sum=100;
    private static Object obj=new Object();

    @Override
    public void run() {
        while (true){//同步代码块
            sum.sleep(1000);
                if (sum>0) {
                    System.out.println(Thread.currentThread().getName()+"售出的票号是"+sum);
                    sum--;
                }else {
                    System.exit(0);
                }
        }
    }
    public synchronized void mp(){

    }

    public static void main(String[] args) {
        TicketDome ticketDome1 = new TicketDome();
        ticketDome1.start();

        TicketDome ticketDome2 = new TicketDome();
        ticketDome2.start();

        TicketDome ticketDome3 = new TicketDome();
        ticketDome3.start();


    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值