美文网首页线程
java并发编程(2)线程常用方法

java并发编程(2)线程常用方法

作者: monkey01 | 来源:发表于2017-09-12 16:42 被阅读23次

    本篇是系列第二篇,主要介绍下java中线程操作常用的函数和隔离区数据保护方法,join()、wait()、notify()、synchronized、volatile。

    1.join()

    当使用join的时候,表示该线程插入,当前线程等待该线程执行完毕
    当线程执行完毕后,被等待的线程会在退出前调用notifyAll通知所有等待线程继续执行。下面我们做个实验来看下join的具体效果,这样比较好理解。

    public class JoinThreadMain {
        public static void main(String[] args) throws InterruptedException {
            Thread thread1 = new Thread(new TestThread());
            thread1.start();
    
            System.out.println("count = " + count);
    
        }
    
        private static int count = 0;
    
        public static class TestThread implements Runnable{
            @Override
            public void run() {
                for(int i=0; i<10000; i++){
                    count++;
                }
            }
        }
    }
    

    从代码看定义了一个static的count变量用于计数,在main函数主线程中调用线程start方法后,接着输出count值,因为start后线程,主线程会直接输出当前的count值,当前count值可能还未执行完线程的循环,所以输出的值一般都是小于10000的,例如下面之行后输出的是63。

    count = 63
    

    如果我们要实现子线程完成后再其他线程再输出,就可以使用join方法将该线程临时插入当前线程去执行,直到执行完毕再继续执行当前线程,修改后的代码如下:

    public class JoinThreadMain {
        public static void main(String[] args) throws InterruptedException {
            Thread thread1 = new Thread(new TestThread());
            thread1.start();
            thread1.join();
            System.out.println("count = " + count);
    
        }
    
        private static int count = 0;
    
        public static class TestThread implements Runnable{
            @Override
            public void run() {
                for(int i=0; i<10000; i++){
                    count++;
                }
            }
        }
    }
    

    现在的输出结果就是我们期望的10000了。

    count = 10000
    

    2.wait()、notify()

    jdk中两个非常重要的接口线程方法,wait和notity,这两个方法不在Thread类而是在Object类。当一个对象实例调用wait方法后,当前线程就会在这个对象上等待,当thread1调用了obj.wait(),那么thread1就会进入等待队列,当obj.notify被调用,系统会从队列中随机选择一个线程继续执行。obj.wait方法不可以随便调用必须包含在对应的synchronzied语句中,无论是wait还是notify都需要先获得目标对象的监视器。

    public class WaitNotifyMain {
        private final static Object object = new Object();
    
        public static void main(String[] args) throws InterruptedException {
            
            Thread thread1 = new Thread(new Thread1());
            Thread thread2 = new Thread(new Thread2());
            thread1.start();
            thread2.start();
    
        }
    
        public static class Thread1 extends Thread {
            public void run() {
                synchronized (object) {
                    System.out.println(System.currentTimeMillis() + ":thread1 start !");
                    try {
                        System.out.println(System.currentTimeMillis() + ":thread1 wait for object !");
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(System.currentTimeMillis() + ":thread1 end!");
                }
            }
        }
    
        public static class Thread2 extends Thread {
            public void run() {
                synchronized (object) {
                    System.out.println(System.currentTimeMillis() + ":thread2 start ! notify one thread");
                    object.notify();
    
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(System.currentTimeMillis() + ":thread2 end!");
                }
            }
        }
    }
    

    3.synchronized

    synchronized是最常用的隔离区保护的关键字,线程中方法如果使用synchronized则表示该方法同时只能有一个线程访问,这就能够保证隔离区数据的线程安全,这也是保证隔离区数据安全最常用的方法,下面定义的add方法不管并发多大计算的结果都不会改变。

    public class SyncThread implements Runnable {
        private synchronized void add(){
            SynchronizedMain.count2++;
        }
    
        @Override
        public void run() {
            for(int i=0; i<100000; i++){
                add();
            }
        }
    }
    

    4.Volatile

    当用volatile去申明变量时,就等于告诉虚拟机,这个变量极有可能会被某些程序或者线程修改,当值修改,所有线程都能看到该值变化。但是执行会发现count还是不等于100000,因为当有线程并发操作count的时候,虽然count修改后通知到各个线程,但是并发读取到值的线程还是会存在修改后覆盖的情况,所以肯定是小于100000,所以volatile不适合用在大并发的场景。

    public class VolatileMain {
        
        static volatile int count = 0;
    
        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 MyVolatileThread());
                threads[i].start();
            }
    
            for(int i=0; i<10; i++){
                threads[i].join();
            }
            
            System.out.println(count);
        }
    
        public static class MyVolatileThread implements Runnable{
            @Override
            public void run() {
                for(int i=0; i<10000; i++){
                    count++;
                }
            }
        }
    
    }
    

    相关文章

      网友评论

        本文标题:java并发编程(2)线程常用方法

        本文链接:https://www.haomeiwen.com/subject/idncsxtx.html