Java多线程(1)

作者: 简祖明 | 来源:发表于2018-03-09 18:31 被阅读29次

    开启线程的三种方式?

    1. 继承Thread类,重写run(),调用start;
    2. 实现Runnable接口,复写run(),将Runnable子类对象传递给Thread类对象,调用start;
    3. 创建FutureTask对象,创建Callable子类对象。重写call(相当于run)方法,将其传递给FutureTask对象(相当于一个Runnable)。创建Thread类对象,将FutureTask对象传递给Thread对象。调用start方法开启线程。这种方式可以获得线程执行完之后的返回值。

    run()和start()方法区别

    run()是一个普通方法,多线程中可以多次调用,start()开启一个线程。jvm内部机制规定在调用start()开启线程时,会自动调用run().

    如何控制某个方法允许并发访问线程的个数?

    创建一个Semaphore对象,初始化访问线程的个数,在线程开始时通过acquire()请求信号,线程完成时release()释放信号即可控制访问线程的个数。

    Java中wait和seelp方法的不同

    sleep()属于Thread类中的,wait()属于Object。
    sleep()睡眠时,保持对象锁,而wait()释放对象锁。wait()通过notify或notifyAll或指定时间来唤醒当前线程(需要获得锁对象),必须放在Snchronized block中,否则会抛出IllegalMonitorStateException

    wait/notify关键字的理解

    • wait( ),notify( ),notifyAll( )属于Object基础类,也就是每个对象都有wait( ),notify( ),notifyAll( ) 的功能,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了。

    • 当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常

    • 当想要调用wait( )进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中。

    • 在while循环里而不是if语句下使用wait,这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知

    • 调用obj.wait( )释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() } 代码段内唤醒A。

    • notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)

    • notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)

    • 假设有三个线程执行了obj.wait( ),那么obj.notifyAll( )则能全部唤醒tread1,thread2,thread3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,tread1,thread2,thread3只有一个有机会获得锁继续执行,例如tread1,其余的需要等待thread1释放obj锁之后才能继续执行。

    • 当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此,thread1,thread2,thread3虽被唤醒,但是仍无法获得obj锁。直到调用线程退出synchronized块,释放obj锁后,thread1,thread2,thread3中的一个才有机会获得锁继续执行。

    什么导致线程阻塞?

    阻塞状态的特点是:该线程放弃CPU的使用,暂停运行,等待阻塞原因消除以后才能恢复运行。或者被其他线程中断退出阻塞状态,同时会抛出InterruptedException。
    导致阻塞的原因有:

    • sleep()进入睡眠状态;
    • 线程执行一段同步代码,未获得相关同步锁,进入阻塞状态;
    • 线程调用对象的wait方法,等待其他线程执行notify()、notifyAll();
    • 线程执行某些IO操作,因为等待相关的资源而进入阻塞状态,比如System. in。

    线程如何关闭?

    1. 使用stop()方法强行终止线程,不推荐,可能发生不可预料的结果;
    2. 使用退出标识,使线程正常退出;
    public class ThreadSafe extends Thread {
        public volatile boolean exit = false; 
            public void run() { 
            while (!exit){
                //do something
            }
        } 
    }
    

    定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值.

    1. 使用interrupt方法中断线程。
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            /*
             * 在这里为一个循环,条件是判断线程的中断标志位是否中断
             */
            while (true&&(!Thread.currentThread().isInterrupted())) {
                try {
                    Log.i("tag","线程运行中"+Thread.currentThread().getId());
                    // 每执行一次暂停40毫秒
                    //当sleep方法抛出InterruptedException  中断状态也会被清掉
                    Thread.sleep(40);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //如果抛出异常则再次设置中断请求
                    Thread.currentThread().interrupt();
                }
            }
        }
    });
    thread.start();
    //触发条件设置中断
    thread.interrupt();
    

    java中同步的几种方式

    1. 同步方法
    2. 同步代码块
    3. 特殊域变量,volatile:
    • 为变量的访问提供了一种免锁机制;
    • 告诉jvm该域可能被其他线程更新;
    • 每次使用都要重新计算,而不是使用寄存器里的值;
    • 不会提供任何原子操作,不能修饰final类型变量。
    1. 使用局部变量ThreadLocal实现
      使用局部变量实现线程同步,如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

    注:ThreadLocal与同步机制

    • ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。
    • 前者采用以”空间换时间”的方法,后者采用以”时间换空间”的方式
    public class Bank{
        //使用ThreadLocal类管理共享变量account
        private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
            @Override
            protected Integer initialValue(){
                return 100;
            }
        };
        public void save(int money){
            account.set(account.get()+money);
        }
        public int getAccount(){
            return account.get();
        }
    }
    
    1. 使用重入锁ReentrantLock
    class Bank {
        private int account = 100;
        //需要声明这个锁
        private Lock lock = new ReentrantLock();
        public int getAccount() {
            return account;
        }
        //这里不再需要synchronized 
        public void save(int money) {
            lock.lock();
            try{
                account += money;
            }finally{
                lock.unlock();
            }
    
        }
    }
    
    1. 使用原子变量实现线程同步。如AtomicInteger,需要线程同步的根本原因就是因为变量的操作不是原子性的

    如何保证线程安全?如何实现线程同步?

    线程同步,同上。

    两个进程同时要求写或者读,能不能实现?如何防止进程的同步?

    1. 允许多个读者同时执行读操作;
    2. 不允许读者、写者同时操作;
    3. 不允许多个写者同时操作。

    java.util.concurrent.locks.ReadWriteLock;
    java.util.concurrent.locks.ReentrantReadWriteLock;

    控制访的数量即可。Java并发库的Semaphore可以完成信号量的控制,Semaphore可以控制某个资源可被同时访问的数量,通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可。

    线程间操作List

    1使用Collections.synchronizedList()构建List;2操作list的方法使用同步锁。
    为何会两者都用,因为Collections.synchronizedList()构建的list只针对list的add(obj)、poll(obj)等方法做同步,在多线程中直接使用方法会同步,但是在操作list时,add(obj)、poll(obj)方法之前不能保证obj是否被其他线程操作过。

    相关文章

      网友评论

        本文标题:Java多线程(1)

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