zoukankan      html  css  js  c++  java
  • Java并发程序设计(二)Java并行程序基础

    Java并行程序基础

    一、线程的生命周期

    其中blocked和waiting的区别:

    作者:赵老师
    链接:https://www.zhihu.com/question/27654579/answer/128050125
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    假设t1,t2先后两个线程,都执行如下代码:
    synchronized(Obj) {
        Obj.wait(); 
    }
    

    t1先进,最后在Obj.wait()下卡住,这时java管t1的状态waitting状态
    t2后进,直接在第一行就卡住了,这时java叫t2为blocked状态。

    请注意,blocked是过去分词,意味着他是被卡住的(无辜啊,全是泪)。因为这段代码只让一条线程运行。同时,jvm是知道怎么结束blocked的,只要别的线程退出这段代码,他就会自动让你进去。也就是说别的线程无需唤醒你,由jvm自动来干

    而waiiting是说我调用wait()等函数,主动卡住自己(我在等一个白富美),请jvm在满足某种条件后(白富美发消息让我们晚上见),比如另条线程调用了notify()后,把我唤醒。这个唤醒的责任在于别的线程(白富美)明确的调用一些唤醒函数。

    做这样的区分,是jvm出于管理的需要,做了这种区分,比如两个原因的线程放两个队列里管理,如果别的线程运行出了synchronized这段代码,我只需要去blocked队列,放个出来。而某人调用了notify(),我只需要去waitting队列里取个出来。

    P.S. 从linux内核来看,这些线程都是等待状态,没区别,区别只在于java的管理需要。通常我们在系统级别说线程的blocked,是说线程操作io,被暂停了,这种线程由linux内核来唤醒(io设备报告数据来了,内核把block的线程放进可运行的进程队列,依次得到处理器时间),而wait是说,等待一个内核mutex对象,另个线程signal这个mutex后,这个线程才可以运行。区别在于由谁唤醒,是操作系统,还是另一个线程,这里倒和java很相似。
     

    二、线程的基本操作

    一)新建线程

    1.方法一:

    Thread t=new Thread();
    t.start(); //新建一个线程,并在该线程中调用t的run()方法。

    2.方法二:

    public Class MyThread implements Runnable{
        
        public static void mian(String[] args){
            Thread t2=new Thread(new MyThread()); 
            t2.start();
        }
        
        @Override
        public void run(){
            System.out.println("I am runnable man!");
        }
    }

    二)终止线程

    1.Thread中的stop()方法会直接终止线程,容易引发线程安全问题已经被弃用。

    2.所以我们采用不立即中断线程,而是给目标线程发送通知:“希望你能中断线程的时候中断啦!”。而什么时候中断取决于线程自身。

    public void Thread.inerrupt(); //通知线程中断,设置中断标志位。
    public boolean Thread.isInterrupted() // 判断线程是否有中断标志位。
    public static boolean Thead.interrupted() //判断线程是否有中断标志位,如果有则清除标志位。
    public static native void sleep(long millis) throws InterruptedException //如果当前线程被设置了中断标志位,调用sleep方法时,
    //就会抛出InterruptedException异常,该异常必须被捕获。

    三)wait和notify

    两个方法都是Object类中的方法,方法签名为:

    public final void wait() throws InterruptException;//线程停止执行转为等待状态,直到其他线程调用了同一实例对象的notify()或notifyAll()方法。
    public final native void notify();//随机选择一个线程,将其唤醒。

    需要强调的是,这两个方法都不能随便调用,它们必须包含在synchronized语句中。

    例如:

    public class SimpleWN {
    
        final static Object obj=new Object();
        public static class T1 extends Thread{
            @Override
            public void run(){
                synchronized (obj){
                    System.out.println(System.currentTimeMillis()+" :T1 start!");
                    try {
                        System.out.println(System.currentTimeMillis()+" :wait for object!");
                        obj.wait();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    System.out.println(System.currentTimeMillis()+" :T1 END!");
                }
            }
        }
    
        public static class T2 extends Thread{
            @Override
            public void run(){
                synchronized (obj){
                    System.out.println(System.currentTimeMillis()+" :T2 start! notify one!");
    
                    System.out.println(System.currentTimeMillis()+" :wait for object!");
                    obj.notify();
    
                    System.out.println(System.currentTimeMillis()+" :T2 END!");
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static void main(String[] args){
            Thread t1=new T1();
            Thread t2=new T2();
            t1.start();
            t2.start();
        }
    }

     四)suspend和resume

    suspend()不会释放锁
    如果加锁发生在resume()之前 ,则死锁发生

    五)等待线程结束(join)和谦让(yield)

    public final void join() throws InterrupteException //一直阻塞当前线程直到目标线(调用此方法的线程)程执行完毕
    
    public final synchronized void join(long millis) throws InterruptException //超出最大时间会继续执行。

    join线程的本质是让调用线程wait()在当前线程对象实例上。因此不要在Thread对象实例上使用wait或者notify等类似的方法,因为这很可能影响系统API的工作。

    public static native void yield() //使当前线程让出CPU,与其他线程参与到CPU资源竞争中。

     三、守护线程(Deamon)

    在后台默默地完成一些系统性的服务,比如垃圾回收线程、 JIT线程就可以理解为守护线程
    当一个Java应用内,只有守护线程时,Java虚拟机就会自然退出。

    设置为守护线程:

    Thread t=new DaemonT();
    t.setDaemon(true);
    t.start();

    四、实现线程同步的基本方法

    关键字synchronized的作用是实现线程间的同步,它的工作是对同步的代码加锁,使得每一次只能有一个线程进入同步块。用法:

    1)对某个实例对象加锁。

    2)对对象的实例方法加锁,相当于对当前实例加锁。

    3)对某个类的静态方法加锁,相当于对当前类加锁。

    public class MyRunnable implements Runnable{
    
        static MyRunnable instance=new MyRunnable();
        static Object object=new Object();
        static int i=0;
        
        @Override
        public void run() {
            for(int j=0;j<1000;j++){
                synchronized (object) {
                    i++;
                }
            }
        }
        
        public static void main(String[] args) throws InterruptedException{
            Thread t1=new Thread(instance);
            Thread t2=new Thread(instance);
            
            t1.start();
            t2.start();
            t1.join();
            System.out.println(i); //1562
            t2.join();
            System.out.println(i); //2000
        }
    }

    没问题:

    public class MyRunnable implements Runnable{
    
        static MyRunnable instance=new MyRunnable();
        static Object object=new Object();
        static int i=0;
        
        public static synchronized void increase(){
            i++;
        }
        
        @Override
        public void run() {
            for(int j=0;j<1000;j++){
                increase();
            }
        }
        
        public static void main(String[] args) throws InterruptedException{
            Thread t1=new Thread(new MyRunnable());
            Thread t2=new Thread(new MyRunnable());
            
            t1.start();
            t2.start();
            t1.join();
            System.out.println(i); //1000
            t2.join();
            System.out.println(i); //2000
        }
    }

     错误的同步方式(自行思考原因):

    public class MyRunnable implements Runnable{
    
        static MyRunnable instance=new MyRunnable();
        static Object object=new Object();
        static int i=0;
        
        public synchronized void increase(){
            i++;
        }
        
        @Override
        public void run() {
            for(int j=0;j<1000;j++){
                increase();
            }
        }
        
        public static void main(String[] args) throws InterruptedException{
            Thread t1=new Thread(new MyRunnable());
            Thread t2=new Thread(new MyRunnable());
            
            t1.start();
            t2.start();
            t1.join();
            System.out.println(i); //1747
            t2.join();
            System.out.println(i); //1747 
        }
    }

     五、隐蔽的错误

    一)并发下的ArrayList

    public class ArrayListTest {
        
        static ArrayList<Integer> list=new ArrayList<>();
        public static class AddThread implements Runnable{
            @Override
            public void run() {
                for(int i=0;i<10000;i++){
                    list.add(i);
                }
            }
        }
        
        public static void main(String[] args) throws InterruptedException{
            Thread t1=new Thread(new AddThread());
            Thread t2=new Thread(new AddThread());
            
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println(list.size());
        }
            /*结果:Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 10
        at java.util.ArrayList.add(Unknown Source)
        at com.tang.test.ArrayListTest$AddThread.run(ArrayListTest.java:12)
        at java.lang.Thread.run(Unknown Source)
        10005*/
    }

    解决办法:Vector代替ArrayList

    二)更诡异的HashMap

    解决办法:ConcurrentHashMap代替HashMap。

    三)错误的加锁

    public class BadLock implements Runnable{
    
        static Integer i=0;
        static BadLock instance=new BadLock();
        @Override
        public void run() {
            for(int j=0;j<10000;j++){
                synchronized (i) {
                    i++;
                } 
            }
        }
        
        public static void main(String[] args) throws InterruptedException{
            Thread t1=new Thread(instance);
            Thread t2=new Thread(instance);
            t1.start(); t2.start();
            t1.join(); t2.join();
            System.out.println(i);
        }
        //结果:12862
    }
    Simple is important!
  • 相关阅读:
     Go is more about software engineering than programming language research.
    perl 处理json 数组格式
    perl 处理json 数组格式
    mongodb 限制ip访问
    mongodb 限制ip访问
    haproxy 访问www.zjdev.com 自动跳转到appserver_8001 对应的nginx
    haproxy 访问www.zjdev.com 自动跳转到appserver_8001 对应的nginx
    docker 数据盘映射方案
    docker 数据盘映射方案
    perl encode_utf8 和decode_utf8
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/7454036.html
Copyright © 2011-2022 走看看