1,创建线程有且仅有两种方式(Thread类注释有说明):继承Thread类和实现Runnable接口,实现父类(父接口)中的run方法即可

Runnable是任务(业务逻辑)的抽象,Thread是线程唯一的抽象

public class NewThread {

    private static class NewThread1 extends Thread{
        @Override
        public void run() {
            super.run();
            System.out.println("创建线程之继承Thread");
        }
    }

    private static class NewThread2 implements Runnable{
        @Override
        public void run() {
            System.out.println("创建线程之实现Runnable");
        }
    }
    public static void main(String[] a){
        NewThread1 thread1 = new NewThread1();
        thread1.start();

        NewThread2 thread2 = new NewThread2();
        new Thread(thread2).start();
    }
}

2,当new出一个线程实例,多次调用start方法时,只有第一次会调用成功,如:

public static void main(String[] a){
        NewThread1 thread1 = new NewThread1();
        thread1.start();
        thread1.start();//多次调用

        NewThread2 thread2 = new NewThread2();
        new Thread(thread2).start();
    }

多次调用会抛出异常

原因:Thread类的start方法,当不是第一次调用时,抛出异常,否则执行native的start0方法

附 Thread类的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 */
            }
        }
    }

4,关闭线程

不提倡使用suspend,resume和stop方法

    suspend (过时):挂起线程,不占用cpu,但是会占用资源,一直保持对锁的占有,易触发死锁

    resume:resume()会将suspend挂起的线程继续执行,当resume先于suspend执行时,suspend的线程将一直挂起,引起死锁

    stop(过时):停止线程,带有强制性,不能保证线程占用的资源会正常释放,易产生数据错误

建议用interrupt方法中断线程,本质是通知线程进行中断,设置线程的中断标志位为true,线程中检查中断标志位

    isInterrupted() 检查中断标志位,判断是不是有中断

    Thread.interrupted() (静态方法)检查中断标志位,判断是不是有中断,并把中断标志位由true改为false(复位)

private static class EndThread extends Thread{
        @Override
        public void run() {
            //需要调用Thread.currentThread()方法来获取系统当前正在执行的一条线程,然后才可以对这个线程进行其他操作
            String name = Thread.currentThread().getName();
            while (!isInterrupted()){
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                    System.out.println("running:"+name+"---interrupt is "+isInterrupted());
                    //捕获中断异常并抛出时,虽然调用了中断,但是此处会把中断标志位改为false,会导致程序一直运行,需要手动再中断一次
                    //(猜测)防止有资源没有释放之类的操作,所以手动操作之后进行中断
                    interrupt();
                }
                System.out.println("running:"+name);
                System.out.println("running:"+name+"---interrupt is "+isInterrupted());
            }
            System.out.println("running:"+name+"---interrupt is "+interrupted());
        }
    }
    public static void main(String[] a) throws Exception{
        EndThread endThread = new EndThread();
        endThread.start();
        Thread.sleep(400);
        endThread.interrupt();
    }

不建议使用自建参数进行判定,在方法被阻塞时会不起作用,如:

建议使用方法:

 

5,线程共享

。。。

6,线程等待与唤醒

wait(long timeout):当前线程进入等待状态,并释放当前线程所持有的锁,timeout为毫秒,当被唤醒之后,才会执行wait后面的代码

notify():唤醒当前对象上的单个线程

notifyAll():唤醒当前对象上是所有线程,建议用notifyAll,因为notify可能发生要通知的线程没通知到 ,一般放在同步代码块的最后执行

sleep(long timeout):使当前线程从运行状态进入到阻塞状态,并不会释放持有的锁,让线程休眠>=timeout的时间,休眠结束后,线程会从阻塞状态变为就绪状态

这几个方法都在Object类中而不在Thread类中

JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了(由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。)

7,ThreadLocal

  提供一个变量的副本,线程的隔离。

  spring在事务的部分用到了ThreadLocal,让每个线程保存自己的连接,不在同一连接的话不会形成事务

8,注意

8.1 synchronized方法要锁同一个对象

   int i = 0;     
        @Override
        public void run() {
            synchronized(i){
                i++;//此处锁不起作用,因为i++时i已经被重新new了一个对象
                //如果要锁的话,可以在外面new一个object或者其他类型的对象放到锁里
                System.out.println("创建线程之实现Runnable");

            }
        }

volatile jdk最轻量的同步机制,只保证可见性(修改被线程发现),不保证原子性(线程安全),适用于一写多读

yield()方法:暂停当前正在执行的线程对象,让出CPU的执行权,不会释放锁,并执行其他线程。

yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

jion()方法:线程实例的join()方法可以使得一个线程在另一个线程结束后再执行,即也就是说使得当前线程可以阻塞其他线程执行;

thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。

比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

回收资源的内容写到finalize方法,但是并不靠谱,他只是一个守护线程,有可能还没调用主线程就停了

 

并行:可以同时运行的任务数 ,假如电脑有8个逻辑处理器,那就可以同时运行8个线程

并发:交替执行,时间片轮转就是一种并发机制

未完待续...

最后修改于 2019-08-19 22:21:20
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付
上一篇