《java高并发程序设计》读书笔记(2)

来源:互联网 发布:网络打鱼游戏平台 编辑:程序博客网 时间:2024/06/15 02:35

有关线程必知

线程&进程
- 线程:轻量级进程是程序执行的最小单位。
- 进程:计算机中程序关于某数据集合上的一次运行活动是系统进行资源分配和调度的基本单位,是操作系统结构的基础,是线程的容器。

线程的基本操作

  • 新建线程
Thread thread=new Thread();thread.run();              new Thread(new Runnable() {            @Override            public void run() {            }        }).run();
  • 终止线程

    • stop():立即终止线程,但是有把执行到一半的程序终止可能会引起数据不一致的问题。(比如某个线程刚获取到锁修改了某个值,就被终止,迫使其释放锁,被其他线程获取锁后又立即将数据修改)
    • interupt():它通知目标线程中断,也就是设置中断标志位,中断标志位表示当前线程已经被中断了。
    • Thread.isInterrupted():它用于判断当前线程是否有被中断。
    • Thread.interrupted():用来判断当前线程的中断状态,但同时会清除当前线程的中断标志位。

    • wait&notify

    • wait:当在一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待。并将该线程进入等待队列,该方法不可以随便调用,必须包含在对应的synchronized语句中。

    • notify:当对象调用notify时,就会从等待队列中随机选择一个线程将其唤醒。这个选择是不公平的,是完全随机的。

    demo:

    package wait_notify;    /**     * ClassName:SimpleWN     * Function:     * date:2017/10/12     *     * @author      * @since jdk 1.8     * wait&notify demo     */    public class SimpleWN {        final static Object object=new Object();        public static class T1 extends Thread{            @Override            public void run() {                synchronized (object){                    System.out.println("T1 START");                    try{                        System.out.println("T1 wait for");                        object.wait();                    }catch (Exception e){                        e.printStackTrace();                    }                    System.out.println(System.currentTimeMillis()+"T1 END");                }            }        }        public static class T2 extends Thread{            @Override            public void run() {                synchronized (object){                    System.out.println("T2 START!");                    try{                        object.notify();                        System.out.println("T2 END");                        Thread.sleep(2000);                    }catch (Exception e){                        e.printStackTrace();                    }                }            }        }        public static void main(String[] args){            Thread thread=new T1();            Thread thread1=new T2();            thread.start();            thread1.start();        }    }
  • 挂起(suspend)&继续执行(resume)线程

    • suspend():导致线程暂停同时不释放任何锁资源,此时如果其他任何线程想要访问被它暂用的锁时,都会被牵连导致无法继续运行,除非在对应线程上执行resumer()操作,被挂起的线程才能继续。
    • resume():让已经暂停的线程继续执行。
    • Tips:注意,如果resume()操作意外地在suspend()前就执行了,那么被挂起的线程可能很难有机会被继续执行,并且更严重的是它所占用的锁就不会被释放,因此会导致整个系统工作不正常。
    • 这种情况的案例demo
    public class BadSuspend {        public static Object object=new Object();        static changeObjectThread t1=new changeObjectThread("t1");        static changeObjectThread t2=new changeObjectThread("t2");        public static class changeObjectThread extends Thread{            changeObjectThread(String name){                super.setName(name);            }            @Override            public void run() {                synchronized (object){                    System.out.println(getName()+"线程已经被挂起");                    Thread.currentThread().suspend();                }            }        }        public static void main(String[] args) throws InterruptedException {            t1.start();            Thread.sleep(100);            t2.start();            t1.resume();            t2.resume();            t1.join();            t2.join();        }    }
  • 等待线程结束(join)和谦让(yeild)

    • join:某个线程要等待另外一个线程执行完毕才能继续执行,利用join来实现这个功能。第一个join()方法会无限等待,它会一直阻碍当前线程,直到目标线程执行完毕。join()的本质是让调用wait()在当前线程对象实例上。其核心代码片段为:
      while(isAlive){
      wait(0);
      }

    • yeild:静态方法,它会使得当前线程让出cpu,注意:让出不表示当前线程不执行了,当先线程在让出cpu后还会进行cpu资源争夺,但是是否能够再次被分配到,就不一定了。因此它好像在说“我已经完成一些重要的工作了,我应该是可以休息一下,让给其他线程一些工作机会了。”

volatile与java内存模型(JMM)

  • volatile:用该关键字申明一个变量时,等于告诉虚拟机这个变量有可能会被某些程序或者线程修改,为了确保这个变量被修改后应用程序范围内的所有线程都能够“看到”这个变动,虚拟机就得采用一定的手段,保证这个变量的可见性。注意:volatile并不能代替锁,它也无法保证一些复合操作的原子性,比如下面例子:
public class VolatileTest {    static volatile int i=0;    public static class PlusTask implements Runnable{        @Override        public void run() {            for(int k=0;k<10000;k++){                i++;//如果这里是原子性的,那么最终的值应该是100000(10个线程各累加10000次)但实际结果小于100000            }        }    }    public static void main(String[] args) throws InterruptedException {        Thread[] threads=new Thread[10];        for(int i=0;i<10;i++){            threads[i]=new Thread(new PlusTask());            threads[i].start();        }        for (int i=0;i<10;i++){            threads[i].join();        }        System.out.println(i);    }}

此外,volatile也能保证数据的可见性和有序性。案例如下:

public class NoVisibility {    private static volatile boolean ready;    private static int number;    private static class ReaderThread extends Thread{        @Override        public void run() {            while (!ready);            System.out.println(number);        }    }    public static void main(String[] args) throws InterruptedException{        new ReaderThread().start();        Thread.sleep(1000);        number=42;        ready=true;        Thread.sleep(10000);    }}

守护线程(Daemon)

在后台默默地完成一些系统性的服务,如垃圾回收,守护线程守护的对象已经不存在了,那么整个应用程序自然结束,因此当一个java应用内,只有守护线程时,java虚拟机就会自然退出。让一个线程成为守护线程:线程引用.setDaemon(true);

线程优先级

java 中的线程可以有自己的优先级,优先级高的线程在竞争资源时会更有优势,更可能强占资源,当然这只是个概率问题,运气不好也可能会抢占失败。java中利用1-10表示线程优先级,一般可以使用内置的三个静态标量表示。Thread.MAX_PRIORITY=10;Thread.MIN_PRIORITY=1Thread.NORM_PRIORITY=5;

线程安全

  • synchronized用法

    • 指定加锁对象,进入同步代码前要获得当前实例的锁
    • 直接作用于实例方法,相当于对当前实例加锁,进入同步代码前要获得当前类的锁。
    • 直接作用于静态方法,相当于对当前类加锁,进入同步代码前要获得当前类的锁。

    synchronized除了用于线程同步,确保线程安全外,还可以保证线程间的可见性和有序性,可见性上完全可以替代volatile,只是使用上没volatile那么方便。