多线程学习以及所踩的坑

多线程

0. 线程的状态

//Thread部分源码
	public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called {@code Object.wait()}
         * on an object is waiting for another thread to call
         * {@code Object.notify()} or {@code Object.notifyAll()} on
         * that object. A thread that has called {@code Thread.join()}
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }
  • 线程的6个状态,即线程的生命周期。
  1. NEW:使用new关键字实例化Thread对象后,还没有调用start()方法
  2. RUNNABLE:线程处于运行状态或者处于就绪状态等待资源
  3. BLOCKED:堵塞,在线程调用wait()方法后等待锁对象进入或重新进入同步块/同步方法
  4. WAITING:等待状态,调用了不会超时的wait()、join()、park()方法,等待其他线程的执行特定的操作,如notify(),notifyAll()方法
  5. TIMED_WAITING:具有等待时间的等待状态,调用了有超时的 wait()、sleep()、join()、parkNanos()、parkUntil()方法
  6. TERMINATED:线程终止,或者正常执行完run方法后

1. 一个线程连续调用两次start(),会有什么结果?

public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    Thread thread=new Thread(myRunnable);
    System.out.println(thread.getState()); //查看线程状态
    thread.start();
    System.out.println(thread.getState()); //查看线程状态
    thread.start();
    System.out.println(thread.getState()); //查看线程状态
}
结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQlA99vL-1588933408700)(D:\张文东\md笔记\JAVA\pic\线程调用两次start方法.png)]

抛出java.lang.IllegalThreadStateException异常,也就是说线程状态出现了异常。

  • 查看start()方法部分源码
public
class Thread implements Runnable {
    ......
    /*
     * Java thread status for tools, default indicates thread 'not yet started'
     */
    private volatile int threadStatus;
    ......
    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    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();
        ......

一个线程被NEW出来后就是NEW新建状态,然后通过调用start()方法进入RUNNABLE就绪状态。

看到start方法的源码注释,It is never legal to start a thread more than once,也就是说一个线程多次调用start方法是非法的。

Thread类有一个变量threadStatus,表示线程的状态,0表示NEW状态。

在start方法里进行了一个判断,如果线程状态不等于0,说明线程状态已经不是NEW了,说明线程至少已经处于就绪状态,再次调用start方法没有意义。

结论

一个线程不能连续调用两次或两次以上的start()方法,否则会抛出java.lang.IllegalThreadStateException异常。

2. 为什么暂停线程废弃了suspend()方法?

在这里插入图片描述

原因

线程在调用suspend()方法暂停后,不会释放锁对象,有可能会导致其他线程一直处于堵塞状态。

3. 为什么会”虚假唤醒“?

原因

多线程交互中可分成三个步骤:1.判断;2.干活;3.通知;

“虚假唤醒”就是因为在调用wait()方法时的判断用了if判断,导致虚假唤醒。

处理

查看wait()在官方API文档中的描述可知:

//中断和虚假唤醒是可能的,并且该方法应该始终在循环中使用
synchronized (obj) {
    while (<condition does not hold>)
        obj.wait();
    ... // Perform action appropriate to condition
} 

即在线程的判断当中用while循环做判断,不要用if判断。

wait()、notify()和notifyAll()方法在我印象中是Thread线程类里面的方法,但是实际上却是在Object类里

4. ⑧锁问题的核心?

问题

一个线程已经在执行中,可能执行需要的时间比较长,此时另一个线程进来,那么这个后进来的线程是马上执行?还是等待第一个线程执行完成后再执行?

原因

关键字synchronized锁的具体对象不一样。

  • 普通方法加上synchronized,锁的是具体的对象。

两个线程操作同一个资源对象,那线程之间就有竞争关系,

两个线程操作两个不同的资源对象,那线程之间就不存在竞争关系。

  • 静态方法加上synchronized,锁的是整个类模板对象。

多个线程操作的资源对象只要是由同一个类模板实例化的,那就有竞争关系。

  • 在代码块用synchronized,锁的对象是synchronized(对象)括号里的对象。

5. 使用Callable接口实现线程

写法与运行结果
public class CallableTest {

	public static void main(String[] args) throws InterruptedException, ExecutionException  {
		MyThread myThread = new MyThread();
		FutureTask futureTask = new FutureTask(myThread);
		new Thread(futureTask, "A").start();
		// get方法获取返回结果,有可能会导致堵塞,还会抛出异常,使用时要放到最后
		System.out.println(futureTask.get());
	}
}

/**
 * 与Runnable接口的不同: 1.方法不同,call/run; 2.有返回值; 3.可以抛出异常
 */
class MyThread implements Callable<Integer> {

	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("call......");
		return 1024;
	}
}

callable

注意
  • 与Runnable接口的不同: 1.方法不同,call/run; 2.有返回值; 3.可以抛出异常

  • get方法获取返回结果,有可能会导致堵塞,还会抛出异常,使用时要放到最后

FutureTask部分源码
public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    // state存放的是FutureTask对象的状态
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;
	......
    /** The result to return or exception to throw from get() */
    //存放返回结果
    private Object outcome; // non-volatile, protected by state reads/writes
    ......
    // VarHandle mechanics
    private static final VarHandle STATE;
    ......
    /**
     * @throws CancellationException {@inheritDoc}
     */
    // 根据状态判断是直接获取返回结果,还是要等待(在外表现为堵塞)
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    ......
}

也就是说同一个FutureTask对象会对线程的返回结果进行一个保存,根据状态判断是NORMAL即直接返回结果,不用再次进行运算。

——未完待续

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值