Java并发编程(1):Java并发编程的基础

Java并发编程基础

一、 多线程初探

先了解两种Java中实现线程的方法:

  1. 继承Thread类
  2. 实现Runnable接口
    严谨的讲它们是一种方式,都是为了构造Thread类,看Thread类的代码就会发现,Thread类自己是实现了Runnable接口的。
public class Thread implements Runnable

1).继承Thread

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

    public static void main(String[] args) {
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start();
    }
}

继承Thread类,重写run方法,然后new出这个类的对象,调用start方法,即可启动该线程。

虚拟机并不是在调用start后虚拟机会创建一个线程,但不一定是当时执行run方法,而是需要在该线程分配到时间片的时候才会执行run方法里的代码。
需要注意的是,一个Thread对象不能调用多次start方法,否则会抛出异常。

2).实现Runnable接口

先看一下Runnable接口的源码

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

首先,看注解发现这是一个函数式接口,也就是说Java8之后可以用Lambda的方式来使用这个接口。
下面再看看如何用实现Runnable接口的方式来创建一个线程:

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

    public static void main(String[] args) {
        new Thread(new ThreadDemo()).start();
    }
}

经过Lambda简化之后的方式:

public class ThreadDemo {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println("Lambda ThreadDemo");
        }).start();
    }
}

即可以不写一个具体类来实现,而是直接用Lambda表达式表示。

2.Thread类介绍

想要熟悉一个类最好的方式是使用它的API和阅读它的源码,我们已经知道了如何构造Thread类来开启一个线程,下面就详细介绍一下Thread类的方法。

1) Thread的构造方法

    /**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, null, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     */
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

Thread类有9个构造方法,写下的两个是我们刚使用过的,可以看到Thread类的构造方法都是通过init构造的。下面看看init方法:

   /**
     * Initializes a Thread with the current AccessControlContext.
     * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

    /**
     * Initializes a Thread.
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize the desired stack size for the new thread, or
     *        zero to indicate that this parameter is to be ignored.
     * @param acc the AccessControlContext to inherit, or
     *            AccessController.getContext() if null
     * @param inheritThreadLocals if {@code true}, inherit initial values for
     *            inheritable thread-locals from the constructing thread
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals)

init方法的5个参数分别是

  1. ThreadGroup g 这个参数顾名思义,表示线程组,用于指定这个线程是在哪个线程组里。
  2. Runnable target 这个参数上面使用过,传入Runnable定义的run方法,用于定义线程需要执行的任务。
  3. String name 用于定义线程的名称
  4. long stackSize 用于定义新线程栈空间的大小,或者传入0则这个值将被忽略。
  5. AccessControlContext acc 查看代码发现acc只在调用Thread的init方法时被赋值给inheritedAccessControlContext这个私有成员变量,而这个私有成员变量也只在Thread的exit()方法中被赋值为null。对于用户来说也只有如下构造方法能给这个成员赋值:
    /**
     * Creates a new Thread that inherits the given AccessControlContext.
     * This is not a public constructor.
     */
    Thread(Runnable target, AccessControlContext acc) {
        init(null, target, "Thread-" + nextThreadNum(), 0, acc);
    }

AccessControlContext用于制定系统资源访问决策基于它封装的上下文。

更具体的说,它封装了一个上下文和一个单独的方法 checkPermission,这个方法和 AccessController 里的checkPermission方法是相同的,只有一个区别就是AccessControlContext的checkPermission基于它封装的上下文作出决策,而不是基于当前执行的线程。

因此AccessControlContext的用途是给那些需要给定上下文的安全检查的情形,实际上是给定的不同的上下文,例如从辅助线程中。
6. boolean inheritThreadLocals 是可继承的ThreadLocal也就是线程组,后面会深入介绍线程组的概念。

2) Thread的常用方法

  1. run()方法和start()方法
    在上面的例子中已经使用了run和start方法,可以看到run方法是用来被用户重写用来执行用户需要该线程执行的任务。start方法是用于启动线程。
  2. Thread.currentThread()静态方法,可以直接返回当前执行的线程Thread类的引用。
  3. yield() 礼让方法,表示当前线程可以让出cpu占用,然后线程继续竞争cpu的时间片,当前线程也会同样参与竞争,所以调用了该方法后,当前线程还是有可能继续执行。
posted @ 2020-11-19 21:53  BarneyMosby  阅读(115)  评论(0)    收藏  举报