开启线程的三种方式?
- 继承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是否被其他线程操作过。
网友评论