zoukankan      html  css  js  c++  java
  • JAVA多线程一

    介绍

    线程是操作系统的最小单位,一个进程可以创建多个线程。
    线程有五种状态,分别是新建、就绪、运行、阻塞、死亡状态。

    多线程可以提高执行效率,但是如果单线程可以完成的任务,使用多线程反而会增加不必要的开销,降低效率。例如将某个数加一百次,使用多线程反而会比单线程耗费的时间多。

    创建线程

    java创建线程有两种方法

    • 继承Thread,重写run函数,调用start方法
    package com.thread;
    
    public class ExtendTreadTest extends Thread {
        int i = 0;
    
        public void run()
        {
            for(;i<100;i++){
                System.out.println(getName()+"  "+i);
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    
        public static void main(String[] args) {
            new ExtendTreadTest().start();
            new ExtendTreadTest().start();
        }
    }
    
    
    • 实现Runnable接口,重写run函数,调用start方法
    package com.thread;
    
    import static java.lang.Thread.sleep;
    
    
    public class ImplementRunnable implements Runnable {
        int i = 0;
    
        public void run()
        {
            for(;i<100;i++){
                System.out.println(Thread.currentThread().getName()+"  "+i);
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    
        public static void main(String[] args) {
            Thread thread = new Thread(new ImplementRunnable(), "test1");
            thread.start();
            Thread thread1 = new Thread(new ImplementRunnable(), "test2");
            thread1.start();
        }
    }
    

    JAVA允许继承一个类,并实现多个接口,所以通常应用中实现Runnable比较好。

    线程间通信

    volatile和synchornized关键字

    volatile

    volatile 会确保线程在每一次使用变量之前都会从共享内存中读取变量的值,然后然后放入自己的工作内存进行处理,处理完成后将新的值立即同步到内存,在这个期间,可能会有其他的线程也对该变量进行处理。所以volatile并不是线程安全的。

    package com.thread;
    
    public class Test {
        public volatile int inc = 0;
    
        public void increase() {
            inc++;
            System.out.println(inc);
        }
    
        public static void main(String[] args) {
            final Test test = new Test();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<10000;j++)
                            test.increase();
                    };
                }.start();
            }
    
            while(Thread.activeCount()>1)  //保证前面的线程都执行完
                Thread.yield();
            System.out.println(test.inc);
        }
    }
    

    例如上面的方法,期望的实验结果是100000,但是实际情况偶尔会出现小于100000的情况。是因为volatile没法保证对变量操作的原子性,inc++不是一个原子操作,就会导致结果出现问题。
    解决的办法有使用synchronized,锁,以及将变量变为原子操作AtomicInteger。
    总体来说,如果能保证对变量的操作是原子性的,那么使用volatile会是一个较好的办法。
    参考:http://www.cnblogs.com/dolphin0520/p/3920373.html

    synchornized

    确保多线程对临界资源的互斥性访问的一种方式是使用synchornized。
    例如上面的例子:

        public synchornized void increase() {
            inc++;
            System.out.println(inc);
        }
    

    或者

        public void increase() {
        	synchornized {
            inc++;
            System.out.println(inc);
            }
        }
    

    上面的两个都是对类的对象做同步,而不是对类本身进行同步。每个线程必须共用一个对象才能够达到同步效果。

        public void increase() {
        	synchornized(Test.class) {
            inc++;
            System.out.println(inc);
            }
        }
    

    上面是对类本身进行同步,对于Test类,它只有一个类定义,同时只有一个线程可以访问increase方法。

    在JAVA中,任意的一个对象都有自己的监视器,当这个对象由同步块或者同步方法调用的时候,执行的线程必须获取该对象的监视器,然后再进入同步块(方法),否则就会进入一个阻塞队列,等待线程退出监视器。

    wait和notify以及notifyAll

    1.调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
    2.调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程
    3.调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程

    package com.thread;
    
    
    import static java.lang.Thread.sleep;
    
    public class WaitNotify {
        static boolean flag = true;
        static Object lock = new Object();
    
        public static void main(String[] args) {
            Thread waitThread = new Thread(new Wait(), "waitThread");
            waitThread.start();
            try {
                sleep(100);
            } catch (Exception err) {
                err.printStackTrace();
            }
            Thread notifyThread = new Thread(new Notify(), "notifyThread");
            notifyThread.start();
    
        }
    
        static class Wait implements Runnable {
            public void run() {
                synchronized (lock) {
                    while (flag) {
                        try {
                            System.out.println(Thread.currentThread().getName() + " wait");
                            lock.wait();
                        } catch (InterruptedException e) {
    
                        }
                    }
                }
                System.out.println(Thread.currentThread().getName() + " run");
            }
        }
    
        static class Notify implements Runnable {
            public void run() {
                synchronized (lock) {
                        try {
                            System.out.println(Thread.currentThread().getName() + " hold lock");
                            sleep(1000);
                            lock.notify();
                            flag = false;
    
                        } catch (InterruptedException e) {
    
                        }
                }
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " run");
            }
        }
    }
    
    

    图中,waitThread首先获取了对象的锁,然后调用对象的wait()方法,从而放弃了锁并进入等待队列WaitQueue中。NotifyThread随后获取了对象的锁,并调用对象的notify方法,将WaitThread从WaitQueue转移到了synchornizedQueue中,然后waitThread转变为了阻塞状态。NotifyThread释放了锁之后,WaitThread再次获得了锁并从wait()方法中返回并继续执行。

    参考:JAVA并发编程的艺术

  • 相关阅读:
    太鼓达人
    DB2中循环日期跑数据
    DB2分区表删除和添加分区
    DB2日常运维之总结(转)
    oracle SQL not in null,单列,复合列
    利用rlwrap配置linux下oracle sqlplus 历史记录回调
    Oracle非默认监听的处理会遇到的问题以及处理方法
    Sqlserver循环嵌套
    rman全备份异机恢复
    Windows系统下Oracle数据库冷备
  • 原文地址:https://www.cnblogs.com/SpeakSoftlyLove/p/5557896.html
Copyright © 2011-2022 走看看