Java多线程实现

一、进程与线程

1.概念

进程:操作系统中一个程序的执行周期

线程:进程中的一个任务。一个进程中可以包含多个线程

2.进程和线程的区别

Ⅰ.每个进程用于自己的一整套变量,进程是操作系统中资源分配的最小单位。线程依托于进程存在,多个线程共享进程的资源,线程是操作系统中任务调度的基本单位

Ⅱ.启动、撤销一个进程的开销要比启动、撤销一个线程大的多(这里突出线程是轻量级的)

Ⅲ.没有进程就没有线程,进程一旦终止,其内的线程全部撤销

二、Java的多线程实现

java实现多线程有三种方式,第一种是继承Thread类;第二种是实现Runnable接口;第三种是实现Callable<V>接口

1.继承Thread类实现多线程

Thread类在java.lang包中,Thread类是线程操作的核心类,JDK1.0提供

继承Thread类实现多线程的具体方式是直接继承Thread类而后重写类中的run()方法(这个run()方法相当于主方法)。

class myThread extends Thread{
    @Override
    public void run() {
        System.out.println();
    }
}

class Test{
    public static void main(String[] args) {
        myThread myThread = new myThread();
        Thread thread = new Thread(myThread);
        thread.start();
    }
}

 

2.Runnable接口实现多线程

实现Runnable接口并重写run()方法

class myThread implements Runnable{
    @Override
    public void run() {
        System.out.println();
    }
}

class Test{
    public static void main(String[] args) {
        myThread myThread = new myThread();
        Thread thread = new Thread(myThread);
        thread.start();
    }
}

 

Ⅰ.Java中多线程的处理就是一个典型的代理设计模式。其中Thread类完成资源调度、系统分配辅助线程业务类;自定义的线程业务类负责真实业务实现。

Ⅱ.使用Runnable接口实现的多线程程序类可以更好地描述资源共享

 

3.Callable<V>接口实现多线程——唯一一个有返回值的线程实现方法

Callable<V>在java.util.concurrent包中,java.util.concurrent包简称juc包,又叫高并发程序包,实现多线程需要实现Callable接口并重写Callable接口的call()方法。

class myThread implements Callable <String>{
    @Override
    public String call() throws Exception {
        return "Callable接口实现多线程";
    }
}

class Test{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask(new  myThread());
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
    }
}

其中多了一个FutureTask的类,为什么要这样写呢?

我们可以看一下前两个实现多线程的方式都是给Thread类的构造方法传进去了一个对象,当然Thread并不是什么对象都能接收,我们可以看一下Thread类都能接收什么对象

可以看到Thread类并不能接收一个Callable<V>对象,我们看一下FutureTask类的源码,

public class FutureTask<V> implements RunnableFuture<V>

FutureTask类实现了RunnableFuture接口,那么RunnableFuture接口又是什么,继续找源码,

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

原来RunnableFuture接口继承了Runnable接口,那么Thread能接收Runnable对象,就可以接收FutureTask对象。其中FutureTask类中的get()方法可以获取到Callable接口call方法的返回值。当线程需要返回值时只能采用Callable接口实现多线程。

注意:无论哪种方式实现多线程,线程的启动一律调用Thread类提供的start()方法。

start()方法解析:

start()方法源码

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

首先检查线程状态是否为0(线程默认状态为0表示线程未启动),如果线程已经启动再次调用start方法会抛出IllegalThreadStateException,即一个线程的start()方法只能调用一次。如果线程状态等于0,调用start0方法,通过start0()真正将线程启动。

   private native void start0();

start0方法是一个本地方法。JVM会调用start0方法进行资源分配与系统调度,准备好资源启动线程后回调run()来执行线程的具体任务。

三、多线程常用操作方法 

1.线程的命名与取得

1.通过构造方法命名

public Thread(String name)

public Thread(Runnable target,String name)

2.设置线程名称 

ublic final synchronized void setName(String name)

3.取得线程名称

public final String getName()

4.取得当前正在执行的线程对象(重要

public static native Thread currentThread();
class Test{
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println("线程");
        }).start();
        Thread.currentThread().setName("A");
        System.out.println(Thread.currentThread().getName());
    }
}

java中的main方法实际上是一个主线程(名字为 main)

四、多线程其他操作方法

1.线程休眠(sleep)——从运行状态到阻塞状态,结束后回到就绪状态

线程休眠:指的是让线程暂缓执行,等到了预计时间再恢复执行

线程休眠会立即交出CPU,让CPU去执行其他任务。线程休眠不会释放对象锁

public static native void sleep(long millis) throws InterruptedException;

long millis单位是毫秒

2.线程让步(yield)——从运行状态返回就绪状态

public static native void yield();

暂停当前正在执行的线程对象,并执行其他线程

yield()会让当前线程交出CPU,但不一定立即交出。yield()交出CPU后只能让拥有相同优先级的线程有获得CPU的机会。yield()不会释放对象锁。

3.join()方法——从运行态到阻塞态

public final void join() throws InterruptedException

等待该线程终止。join()方法会释放锁。如果在主线程中调用该方法会让主线程休眠,让调用该方法的线程先执行完毕后再恢复执行主线程。join()只是对Object类wait()方法做了一层包装

4.线程停止(三种方式)

1)手工设置标记位,让线程在满足条件后退出

class myThread implements Runnable{
    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println(Thread.currentThread().getName() + "线程" + i++);
        }
    }
    public void setFlag(boolean flag){
        this.flag = flag;
    }
}
public class Test{
    public static void main(String[] args) {
        myThread mt = new myThread();
        Thread thread = new Thread(mt,"A");
        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mt.setFlag(false);
    }
}

2)使用stop()方法强制让线程退出,但是该方法不安全,已经被@Deprecated

class myThread implements Runnable{
    private boolean flag = true;
    @Override
    public void run() {
        int x = 0;
        int y = 0;
        while (flag) {
            System.out.println(x++);
            System.out.println(y++);
        }
    }
}

public class Test{
    public static void main(String[] args) {
        myThread mt = new myThread();
        Thread thread = new Thread(mt,"A");
        thread.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.stop();
    }
}

我们多次运行这段代码就会发现有时候最后一个y没有输出,y其实也没有进行自增操作,那么在我们想让x和y变化一致的情况下,这段代码就是不安全的。即使进入到了循环中,调用stop方法也会立即停止,不会等循环执行完。

stop会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,假如一个线程正在执行:synchronized void { x = 3; y = 4;} 由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也会马上stop了,这样就产生了不完整的残废数据。

3)使用Thread类提供的interrupt()中断线程(就是系统设置了一个标记位,直接使用即可)

interrupt方法只是将线程的状态改为中断状态而已,它不会中断一个正在运行的线程,这一方法实际完成的是,给受阻塞的线
程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。

如果线程调用了wait、sleep()、join()方法进入阻塞态,调用该进程的interrupt()会抛出InterruptedException,并且将线程interrupt重置为false。调用线程类的interrupted方法,其本质只是设置该线程的中断标志,将中断标志设置为true,并根据线程状态决定是否抛出异常。

class MyThread implements Runnable {
    private boolean flag = true;
    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
                /**
                 * 这里阻塞之后,线程被调用了interrupte()方法,
                 * 清除中断标志,就会抛出一个异常
                 * java.lang.InterruptedException
                 */
                Thread.sleep(1000);
                boolean bool = Thread.currentThread().isInterrupted();
                if (bool) {
                    System.out.println("非阻塞情况下执行该操作。。。线程状态" + bool);
                    break;
                }
                System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                System.out.println("退出了");
                /**
                 * 这里退出阻塞状态,且中断标志被系统会自动清除,
                 * 并且重新设置为false,所以此处bool为false
                 */
                boolean bool = Thread.currentThread().isInterrupted();
                System.out.println(bool);
                    //退出run方法,中断进程
                return;
            }
        }
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread, "子线程A");
        thread1.start();
        Thread.sleep(3000);
        thread1.interrupt();
        System.out.println("代码结束");
    }
}

 

5.线程优先级(比较鸡肋)(范围[1 - 10])

线程的优先级是指,优先级越高越有可能先执行而已,仅仅是有可能

设置优先级

public final void setPriority(int newPriority)

取得优先级

public final int getPriority()

Thread提供的优先级              

Thread.MAX_PRIORITY = 10;

Thread.NORM_PRIORITY = 5;

Thread.MIN_PRIORITY = 1;

main线程的优先级是5

线程的继承性:优先级可以继承

在A线程中启动B线程,则A和B的优先级一样

五、守护线程(后台线程)

守护线程是一种特殊的线程,属于陪伴线程。java中一共两种线程,一种是用户线程,一种是守护线程

isDaemon()判断是否是守护线程

典型的守护线程:垃圾回收线程

只要当前JVM进程中存在任何一个用户线程没有结束,守护线程就一直在工作;只有当最后一个用户线程停止后,守护线程会随着JVM进程一同停止。

setDaemon()将当前线程设置成为守护线程

java中启动线程默认为用户线程,包括我们启动的主线程

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值