开启线程的三种方式?
继承Thread类,重写run(),调用start;
实现Runnable接口,复写run(),将Runnable子类对象传递给Thread类对象,调用start;
创建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。
线程如何关闭?
使用stop()方法强行终止线程,不推荐,可能发生不可预料的结果;
使用退出标识,使线程正常退出;
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的值.
使用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中同步的几种方式
同步方法
同步代码块
特殊域变量,volatile:
为变量的访问提供了一种免锁机制;
告诉jvm该域可能被其他线程更新;
每次使用都要重新计算,而不是使用寄存器里的值;
不会提供任何原子操作,不能修饰final类型变量。
使用局部变量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(); } }
使用重入锁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(); } } }
使用原子变量实现线程同步。如AtomicInteger,需要线程同步的根本原因就是因为变量的操作不是原子性的
如何保证线程安全?如何实现线程同步?
线程同步,同上。
两个进程同时要求写或者读,能不能实现?如何防止进程的同步?
允许多个读者同时执行读操作;
不允许读者、写者同时操作;
不允许多个写者同时操作。
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
共同学习,写下你的评论
评论加载中...
作者其他优质文章