Java多线程(1)

开启线程的三种方式?

  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是否被其他线程操作过。

    原文作者:简祖明
    原文地址: https://www.jianshu.com/p/c93b1898be53#comments
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞