进程和多线程
- 进程是资源分配的最小单元,有自己独立的空间。进程之间通信更复杂需要(IPC)。
- 线程是程序执行的最小单元,多个线程在同一进程里面共享资源。
线程实现方式
- 新建
Thread
类创建线程:
Thread thread = new Thread(){
@Override
public void run() {
super.run();
}
};
thread.start();
- 实现
Runnable
接口创建线程:
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
Thread thread = new Thread(runnable);
thread.start();
- 利用线程工厂创建线程:
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
Thread thread = factory.newThread(runnable);
thread.start();
- 使用线程池创建线程:
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
Executor executor = Executors.newCachedThreadPool();
executor.execute(runnable);
- 使用
Callable
创建线程:
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "";
}
};
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(callable);
try {
String result = future.get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
多线程通信:
volatile
关键字保证可见性:
比如在两个线程中,其中一个线程对一个变量做操作(原子操作),另一个变量会马上得到新的值。
原子性:一段代码要么不执行,要不就执行完毕。
比如两个线程都同时对一个变量进行赋值并且不是一步操作完成比如:
count++
。这样不是原子操作,当两个线程进行切换的时候,如果不是原子操作,可能会导致一个线程运行到一半,切换到另一个线程进行变量操作,所以要使用synchronized
保证原子性。
其他保证线程安全
ReentrantLock
可用重用锁,当不使用的时候需要unlock
。
ReentrantReadWriteLock
读写锁。
wait
wait
是Object
类的方法,使用wait
必须要在synchronized
修饰的方法或者代码块中,当对象调用wait
方法表示放弃该锁持有权,其他持有此对象监视器的线程执行:
private final Object object = new Object();
private void getWeather(){
synchronized(object){
.....
object.wait()
//what to do
...
}
}
上图中
thread1
和thread2
同时符合条件都进行了wait
,随后同时唤醒,两个线程都会同时唤醒竞争,如果thread2
优先执行,但是改变了thread1
的唤醒条件,这样thread1
是不能被唤醒的,但是thread1
却被唤醒了,这样就造成了虚假唤醒。为了防止线程被虚假唤醒,所以要一直判断是否符合条件再进行
wait
,假如getWeather
是返回weather
,如果weather
是null
则wait
,所以上面代码要改成:
private final Object object = new Object();
private void getWeather(){
synchronized(object){
.....
while(weather == null){
object.wait()
}
//what to do
...
}
}
当
object
调用wait
方法时,表示调用getWeather
方法释放了锁,其他使用object
的监视器的线程竞争结束后胜出的线程执行,所以getWeather
方法会在调用wait
方法后what to do
先不执行,而是其他持有object
的监视器的线程执行。
notify/notifyAll
调用object
的wait
方法后,可以使用notify
来唤醒那些wait
的线程,或者使用notifyAll
来唤醒所有object
监视器的线程。
private void setWeather(){
synchronized(object){
object.notify()
}
}
唤醒之后,不是立即执行和持有object
监视器的所有线程来竞争,最后竞争胜出之后,再继续执行wait
方法以后的代码。
join使用
join
表示当前线程执行完毕才会执行后面的代码:
public void runTest() {
Thread thread1 = new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
getUserName();
}
};
Thread thread2 = new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setUserName("zhangsan");
}
};
thread2.start();
try {
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.start();
}
第一个线程先睡眠1000
再获得username
,第二个线程先睡眠2000
,再设置username
,所以正常逻辑应该是先设置再获取,所以这样知道线程执行顺序的时候可以先让设置的线程执行完毕之后再执行获取线程。
sleep
sleep
是线程的静态方法,表示当前线程睡眠多少时间,但是就算是方法中使用了synchronize
的关键字,使用sleep
也不会释放锁,而是继续持有该锁,直到sleep
结束。