这里写自定义目录标题
进程与线程
进程
程序由指令和数据组成,程序要运行就必须把指令加载至CPU,数据存到内存中。当一个程序被运行,从磁盘加载代码至内存就开启了一个进程。
线程
线程就是一个指令流,在CPU中按一定顺序执行。
一个进程中可以有多个线程。可以理解为线程是进程的子集。
JAVA线程
创建和运行线程
方法一:thread
Thread thread1 =new Thread("t1"){
public void run(){
log.debug("线程一.....");
}
};
thread1.start();
方法二:Runnable配合Thread
Runnable runnable =new Runnable() {
@Override
public void run() {
log.debug("线程二.....");
}
};
Thread thread2 =new Thread(runnable,"t2");
thread2.start();
使用FutureTask 用来创建有返回值的线程
FutureTask<String>task=new FutureTask<>(()->{
return "hello world";
});
new Thread(task,"t3").start();
Thread和Runnable的关系
thread把线程和方法结合在一起了,Runnable把方法和线程分开,
更容易和线程池和api结合。
常见方法
start() | 启动一个新线程,但不会立即执行,等待CPU时间片分给他 |
---|---|
run() | 新线程启动会调用的方法 |
join() | 等待线程运行结束 |
join(long n) | 等待线程运行结束,最多等待n毫秒 |
getPriority() | 获取线程优先级 |
setPriority() | 设置线程优先级 |
interrupt() | 打断先程(设置打断标记) |
interrupted() | 判断当前线程是否被打断(清除打断标记) |
isInterrupted() | 判断当前线程是否被打断(不会打断标记) |
currentThread() | 获取当前线程 |
start()和run()
1.run运行
Thread thread =new Thread("t1"){
public void run(){
log.debug("线程运行,,,");
}
};
thread.run();
log.debug("主线程运行");
结果:
2.start运行
Thread thread =new Thread("t1"){
public void run(){
log.debug("线程运行,,,");
}
};
thread.start();
log.debug("主线程运行");
直接用run()没有启动新的线程,启用start()启用新的线程
join方法
思考一下下面代码运行结果是什么?
static int number =0;
public static void main(String[] args) {
test1();
}
public static void test1(){
Thread t1 =new Thread(()->{
log.debug("线程开始");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
number=10;
},"t1");
t1.start();
log.debug("number={}",number);
}
分析:
因为主线程和t1线程是并行执行的,t1线程1毫秒之后菜鸟算出number=10;
二主线程一开始就打印number
在t1.start()后加join运行结果为:
总结:join需要等待结果返回才能才能继续运行
interrupt
1.打断sleep,wait,join线程会使线程进入阻塞状态
Thread thread1=new Thread(()->{
try {
log.debug("线程开始");
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
Thread.sleep(1);
log.debug("打断状态:{}",thread1.isInterrupted());
thread1.interrupt();
log.debug("打断状态:{}",thread1.isInterrupted());
}
结果:
2.打断正常运行的线程不会清空打断状态
Thread thread1 =new Thread(()->{
while (true){
Thread current =Thread.currentThread();
boolean interrupted = current.isInterrupted();
if (interrupted){
log.debug("打断状态:{}",interrupted);
break;
}
}
});
thread1.start();
Thread.sleep(1);
thread1.interrupt();
结果:
3.打断pork线程不会清空打断状态
Thread thread2 =new Thread(()->{
log.debug("pork...");
LockSupport.park();
log.debug("打断状态:{}",Thread.currentThread().isInterrupted());
});thread2.start();
Thread.sleep(1);
thread2.interrupt();
结果:
主线程与守护线程
默认情况下线程要运行结束才停止运行,而有一种特殊的线程叫守护线程,只有非守护线程运行结束,即使守护线程没有执行完也会强制结束。
共享模型之管程
synchronized
问题:
二个线程一个做自增1000次,一个自加1000次,结果为0吗?
Thread thread1 =new Thread(()->{
for (int i=0;i<5000;i++){
number++;
}
},"t1");
Thread thread2 =new Thread(()->{
for (int i=0;i<5000;i++){
number--;
}
},"t2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
log.debug("number=:{}",number);
以上结果为正,负,0,三种结果。因为JAVA对静态变量的自增自减不是原子操作。
多个线程访问共享资源并做读写操作时会出现问题,这段代码块称为临界区。
解决方案:
1.阻塞式的解决方案:synchronized,lock;
1.非阻塞式的解决方案:原子操作;
synchornized:即俗称的【对象锁】,它采用互斥的方式让同一
时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换.
static int number=0;
public static Object room =new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 =new Thread(()->{
synchronized (room){
for (int i=0;i<5000;i++){
number++;
}}
},"t1");
Thread thread2 =new Thread(()->{
synchronized (room){
for (int i=0;i<5000;i++){
number--;
}}
},"t2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
log.debug("number=:{}",number);
}
结果为0
synchronized好比一个房间,t1线程执行到synchronize(room)时,进入房间并锁了门,t2线程也执行到synchronize(room)只能在外面等待,防止上下文切换。
线程八锁
变量的线程安全
1.静态变量和成员变量没有共享则线程安全,如果被共享且有读写操作则要考虑线程安全问题。
2.局部变量线程安全,但局部变量引用的对象未必线程安全。
2.1如果该对象没有逃离方法的作用访问,它是线程安全的
2.2如果该对象逃离方法的作用范围,需要考虑线程安全
wait
api::
obj.wait() 让进入 object 监视器的线程到 waitSet 等待
obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
public static Object room=new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(()->{
synchronized (room){
log.debug("线程1执行...");
try {
room.wait();//线程一直等待
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其他代码");
}
},"t1");
Thread thread2=new Thread(()->{
synchronized (room){
log.debug("线程2执行...");
try {
room.wait();//线程一直等待
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("其他代码");
}
},"t2");
thread1.start();
thread2.start();
Thread.sleep(2);
synchronized (room){
room.notify();//随机唤醒一个等待线程
// room.notifyAll();// room.notifyAll();//唤醒所有等待线程
}
sleep和wait的区别
- sleep 是 Thread 方法,而 wait 是 Object 的方法
- sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
- sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
public static Object room =new Object();
public static boolean food=false;
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(()->{
synchronized (room){
log.debug("有食物吗{}",food);
if (!food){
log.debug("饿了,先歇会");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有食物吗{}",food);
if (food){
log.debug("可以干活了");
}
}
},"t1");
thread1.start();
for (int i=0;i<5;i++){
new Thread(()->{
synchronized (room){
log.debug("其他线程...");
}
},"ts").start();
}
Thread.sleep(1);
food=true;
}
其它干活的线程,都要一直阻塞,效率太低
public static Object room =new Object();
public static boolean food=false;
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(()->{
synchronized (room){
log.debug("有食物吗{}",food);
if (!food){
log.debug("饿了,先歇会");
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("有食物吗{}",food);
if (food){
log.debug("可以干活了");
}
}
},"t1");
thread1.start();
for (int i=0;i<5;i++){
new Thread(()->{
synchronized (room){
log.debug("其他线程...");
}
},"ts").start();
}
Thread.sleep(100);
food=true;
synchronized (room){
room.notifyAll();
}
}
park和unpark
基本使用:
LockSupport.park();
LockSupport.unpark();
特点:
1)wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
2)park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
3)park & unpark 可以先 unpark,而 wait & notify 不能先 notify
线程状态转换
情况 1 NEW --> RUNNABLE
当调用 t.start() 方法时,由 NEW --> RUNNABLE
情况 2 RUNNABLE <–> WAITING
t 线程用 synchronized(obj) 获取了对象锁后
调用 obj.wait() 方法时,t 线程从 RUNNABLE --> WAITING
调用 obj.notify() , obj.notifyAll() , t.interrupt() 时
竞争锁成功,t 线程从 WAITING --> RUNNABLE
竞争锁失败,t 线程从 WAITING --> BLOCKED
情况 3 RUNNABLE <–> WAITING
当前线程调用 t.join() 方法时,当前线程从 RUNNABLE --> WAITING
注意是当前线程在t 线程对象的监视器上等待
t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 WAITING --> RUNNABLE
情况 4 RUNNABLE <–> WAITING
当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE --> WAITING
调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,会让目标线程从 WAITING -->
RUNNABLE
情况 5 RUNNABLE <–> TIMED_WAITING
t 线程用 synchronized(obj) 获取了对象锁后
调用 obj.wait(long n) 方法时,t 线程从 RUNNABLE --> TIMED_WAITING
t 线程等待时间超过了 n 毫秒,或调用 obj.notify() , obj.notifyAll() , t.interrupt() 时
竞争锁成功,t 线程从 TIMED_WAITING --> RUNNABLE
竞争锁失败,t 线程从 TIMED_WAITING --> BLOCKED
情况 6 RUNNABLE <–> TIMED_WAITING
当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE --> TIMED_WAITING
注意是当前线程在t 线程对象的监视器上等待
当前线程等待时间超过了 n 毫秒,或t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从TIMED_WAITING --> RUNNABLE
情况 7 RUNNABLE <–> TIMED_WAITING
当前线程调用 Thread.sleep(long n) ,当前线程从 RUNNABLE --> TIMED_WAITING
当前线程等待时间超过了 n 毫秒,当前线程从 TIMED_WAITING --> RUNNABLE
情况 8 RUNNABLE <–> TIMED_WAITING
当前线程调用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 时,当前线
程从 RUNNABLE --> TIMED_WAITING
调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,或是等待超时,会让目标线程从
TIMED_WAITING–> RUNNABLE
情况 9 RUNNABLE <–> BLOCKED
t 线程用 synchronized(obj) 获取了对象锁时如果竞争失败,从 RUNNABLE --> BLOCKED
持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,如果其中 t 线程竞争
成功,从 BLOCKED --> RUNNABLE ,其它失败的线程仍然 BLOCKED
情况 10 RUNNABLE <–> TERMINATED
当前线程所有代码运行完毕,进入 TERMINATED