获取方式:
http://www.java1234.com/a/javabook/javabase/2018/1224/12627.html
同步就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。
异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。当这个调用完成后,一般通过状态、通知和回调来通知调用者。对于异步调用,调用的返回并不受调用者控制
并发就是串行分割任务执行,并行是完全同时执行
如果只有一个cpu频率很高,把两件事情分割n,m份,穿插去执行,一下A,一下B,人看来就是感觉是A,B在同时执行,实际是并发
但如果有两个cpu,一个cpu跑A,一个CPU跑B,人看来也是A,B在同时进行,但这个是并行
指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待。
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
多线程中最差的一种情况,多个线程相互占用对方的资源的锁,而又相互等对方释放锁,此时若无外力干预,这些线程则一直处理阻塞的假死状态,形成死锁。
死锁产生的4个必要条件:
1、互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2、占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3、不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
4、循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
https://blog.csdn.net/wljliujuan/article/details/79614019
低优先级的线程长时间获取不到临界区运行而挂起的现象
多个线程主动都互相谦让临界区给对方,导致都拿不到临界区运行的现象
即没有对资源进行锁定,即所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。
无锁典型的特点就是一个修改操作在一个循环内进行,线程会不断的尝试修改共享资源,如果没有冲突就修改成功并退出否则就会继续下一次循环尝试。所以,如果有多个线程修改同一个值必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。
java/lang/Thread.java中有枚举出线程的6种状态:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
状态名称 | 说明 |
---|---|
NEW | 初始状态,线程被构建,但是还没有调用start()方法 |
RUNNABLE | 运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称作“运行中” |
BLOCKED | 阻塞状态,表示线程阻塞于锁 |
WAITING | 等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) |
TIME_WAITING | 超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回的 |
TERMINATED | 终止状态,表示当前线程已经执行完毕 |
new 一个thread并通过start起来
调用Thread的start方法和run方法的区别:
调用run方法是在当前调用线程中串行执行,相当于简单的调用了Thread的一个方法,未新开一个线程跑;
看start方法调用过程,的确是重新创建了一个新的线程在跑:
java/lang/Thread.java
public synchronized void start() {
boolean started = false;
try {
start0();
started = true;
} finally {
}
}
private native void start0();
libcore/ojluni/src/main/native/Thread.c
static JNINativeMethod methods[] = {
{"start0", "(JZ)V", (void *)&JVM_StartThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(Ljava/lang/Object;J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
art/runtime/openjdkjvm/OpenjdkJvm.cc
JNIEXPORT void JVM_StartThread(JNIEnv* env, jobject jthread, jlong stack_size, jboolean daemon) {
art::Thread::CreateNativeThread(env, jthread, stack_size, daemon == JNI_TRUE);
}
runtime/thread.cc
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast(env)->self;
Thread* child_thread = new Thread(is_daemon);
...
// 创建JNIEnvExt Try to allocate a JNIEnvExt for the thread.
std::unique_ptr child_jni_env_ext(
JNIEnvExt::Create(child_thread, Runtime::Current()->GetJavaVM(), &error_msg));
...
int pthread_create_result = 0;
if (child_jni_env_ext.get() != nullptr) {
...
// 创建线程
pthread_create_result = pthread_create(&new_pthread,
&attr,
Thread::CreateCallback,
child_thread);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
...
}
}
Thread.stop()方法在结束线程时,会直接终止线程,并且会立即释放这个线程锁持有的锁。如果线程写数据写了一半被终止,其他线程就有可能获取到锁而可以读操作,结果读出来一个写了一半的错误值。
该方法已经Deprecated,如果不是很清楚确定stop操作不会带来负面影响,就不要使用了。
Thread.interrupt()方法调用后线程并不会立即退出,而是会给线程发一个通知,而目标线程收到通知后如何操作取决与目标线程自己的操作
如果收到中断后需要退出需要目标线程自己实现退出操作,如果中断收到通知后线程无条件stop退出的话就会遇到上面的风险。
在Thread.sleep()方法使用时需要catch一个InterruptedException,即中断异常处理,Thread.sleep() 抛出中断异常之后会清除中断标记,如果不加处理,在下一次循环时则无法处理这个中断,故在异常处理位置再次设置中断标记位。
另外,在捕获中断异常之后做一些数据正确性的回退或设置标志等操作来避免操作一半退出其他线程获得锁来读出错误数据的情况。
wait()和notify()这两个方法不是Thread中的,而是object类中的,意味着任何对象都可以调用这两个方法。
如果一个线程调用来object.wait()方法,那么它就会进入该object的等待队列中,这个等待队列中可能有多个等待该object的线程;
当object.notify()调用之后,会从这个等待队列中随机选择一个线程并将其唤醒,这个选择是不公平的,不是谁先来谁先被调度,而是完全是随机的。object.notifyAll()则是唤醒这个队列中的所有线程。
object.wait()方法不能随意调用,需要在锁内,无论wait()还是notify()都需要先获得对应的锁才能操作。
object.wait() 和Thread.sleep()都可以让线程等待若干时间,但是不同是objetc.wait()会释放目标对象锁,而Thread.sleep()不会释放任何资源。
废弃方法,由于suspend()在导致线程暂停的同时不会释放任何锁资源,会牵连其他线程获得锁。另外suspend()之后的线程状态依然是Runnable。
在调用线程中调用T1的join()方法,调用线程会等待T1运行完毕退出后在继续执行下面的操作。join(long millis)可以指定等到超时。
join()的本质上让调用线程wait()在目标线程的对象实例上,当目标线程执行完,目标线程在退出志强调用notifyAll()方法通知等待线程继续执行。
yield()方法调用可以会使目标线程让出CPU:
yield是一个静态的原生(native)方法
yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态
正确使用 Volatile 变量:https://www.ibm.com/developerworks/cn/java/j-jtp06197.html
可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。
让你彻底理解Synchronized:https://www.jianshu.com/p/d53bf830fa09
线程安全和锁Synchronized概念:https://blog.csdn.net/xlgen157387/article/details/77920497
重入锁:ReentrantLock 详解:https://blog.csdn.net/Somhu/article/details/78874634
和ReentrantLock配合使用,ArrayBlockingQueue即是两者配合实现
基本使用,具体可以看
java并发编程之Condition:https://www.jianshu.com/p/be2dc7c878dc
Java并发——使用Condition线程间通信:https://www.cnblogs.com/shijiaqi1066/p/3412346.html
void await() throws InterruptedException //当前线程进入等待状态,直到被通知(signal)或者被中断时,当前线程进入运行状态,从await()返回;
boolean await(long time, TimeUnit unit) throws InterruptedException//
同样是在接口1的返回条件基础上增加了超时响应,与接口3不同的是:
可以自定义超时时间单位;
void signal()
唤醒一个等待在Condition上的线程;
void signalAll()
唤醒等待在Condition上所有的线程。
Java之 Semaphore信号量的原理和示例:https://blog.csdn.net/Emmanuel__/article/details/78586945
允许多个线程同时访问,Synchronized和ReentrantLock 一次只允许一个线程访问一个资源,而信号量可以指定多个线程,同时访问某一个资源。
// 创建具有给定的许可数和非公平的公平设置的 Semaphore。
Semaphore(int permits)
// 创建具有给定的许可数和给定的公平设置的 Semaphore。
Semaphore(int permits, boolean fair)
// 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void acquire()
// 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。
void acquire(int permits)
// 从此信号量中获取许可,在有可用的许可前将其阻塞。
void acquireUninterruptibly()
// 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。
void acquireUninterruptibly(int permits)
// 返回此信号量中当前可用的许可数。
【Java并发】ReadWriteLock读写锁的使用:https://www.jianshu.com/p/9cd5212c8841
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
两种常用的线程计数器CountDownLatch和循环屏障CyclicBarrier:https://blog.csdn.net/xlgen157387/article/details/78218736
CountDownLatch是一个非常实用的多线程控制工具类,称之为“倒计时器”,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。
CyclicBarrier是另一种多线程并发控制使用工具,和CountDownLatch非常类似,他也可以实现线程间的计数等待,但他的功能要比CountDownLatch更加强大一些。
循环屏障CyclicBarrier以及和CountDownLatch的区别:
https://www.cnblogs.com/twoheads/p/9555867.html
LockSupport里面的park和unpark:https://blog.csdn.net/anLA_/article/details/78635300
LockSupport:https://www.cnblogs.com/skywang12345/p/3505784.html
LockSupport原理剖析:https://segmentfault.com/a/1190000008420938
线程私有,线程安全
AtomicInteger,CAS指令进行操作,线程安全
AtomicReference
AtomicIntegerFieldUpdater
Java 中一些常用的线程安全类型:
类型 | 非线程安全 | 线程安全 | 备注 |
---|---|---|---|
数组 | Arraylist | CopyOnWriteArrayList | |
数组 | vector/stack | stack继承自vector,vector是线程安全的,stack也是 | |
map类型 | Hashmap | ConcurrentHashmap | |
map类型 | ConcurrentSkipListMap | ||
队列 | LinkedList | ConcurrentLinkedQueue | |
队列 | BlockingQueue |
当一个应用进程仅存在守护进程时,Java虚拟机就会自然退出。通过Thread.setDaemon()进行设置。
需注意设置需要在线程start()调用之前设置,否则只是一个普通的用户线程。
通过Thread.setPriority()方法进行设置,值从1到10,数字越大优先级越高。
新建线程组对象,在之后创建线程可作为参数将一些线程指定为一组
JDK提供一套线程池框架 Executor
----未完待续—