线程 Thread
1)应用程序以进程为单位运行,一个进程之内可以分为一到多个线程
2)操作系统有个组件叫任务调度器,将cpu的时间分给不同程序使用,微观串行(单核),宏观并行(多核)
3)可以使用jconsle 来查看某个java进程中线程的运行情况,包括死锁等
好处:
1. 多进程,多线程可以让程序不被阻塞.
2. 充分利用多核cpu的优势,提高运行效率
课程要求
1.掌握创建线程的方法和常用的sleep,join方法等
2.掌握synchronized解决并发问题,熟悉使用wait,notify方法
3.了解死锁,线程的五种状态
多线程
Thread类
创建线程
方法一:
Thread t = new Thread(){
public void run(){
//执行代码,注意不能抛出检查异常
}
}
t.start; //启动线程,注意 : 多次调用会出现IllegalThreadStateException
方法二:
Runnable r = new Runnable() {
public void run(){
// 要执行的代码
}
};
Thread t = new Thread( r );
t.start();
注意 :
直接调用run和使用start间接调用run的区别:
1) 直接调用run是在主线程中执行了run,没有启动新的线程
2) 使用start是启动新的线程,通过新的线程间接执行run
线程中常见方法
Thread.sleep(long n); // 让当前线程休眠n毫秒
Thread.currentThread(); // 找到当前线程
join() : 等待该线程结束
join(long n ) : 等待该线程结束,但最多等待n毫秒
其它方法:
getName() : 得到线程的名称
yield() : 谦让
不推荐使用:
stop(),suspend,resume()
interrupt():
打断正在等待的线程(例如sleep,join的等待)
Thread t = new Thread(()->{
try {
Thread.sleep(5000); // 被打断线程会抛出InterruptedException
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束 。。。");
});
t.start();
Thread.sleep(1000); //休眠一秒
System.out.println("打断t线程。。。");
t.interrupt();
守护线程
setDaemon(true)
默认情况下,java进程需要等待所有线程都运行结束,才会结束
而守护线程,只要主线程运行结束,即使守护线程未执行完,也会跟着主线程一起结束
Thread t1= new Thread(()->{
System.out.println("守护线程开始执行...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("守护线程结束");
});
t1.setDaemon(true); // 设置该线程为守护线程
t1.start();
Thread.sleep(1000);
线程的并发(Concurrent)
synchronized(同步关键字)
语法
synchronized(对象) {
要作为原子操作代码
}
作用 : 解决并发问题
static int i = 0;
static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
synchronized (obj) {
i++;
}
}
});
Thread t2 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
synchronized (obj) {
i--;
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}//如果不处理并发,最终的值将不是0
每个对象有自己的monitor,当一个线程调用synchronized(对象),就相当于进入了这个对象的监视器。要检查有没有owner,如果没有,此线程成为owner; 但如果已经有owner了,这个线程在entryset的区域等待owner的位置空出来。图解如下:
注意 :1)synchronized必须是进入同一个对象的monitor 才有上述的效果
2)两个线程进入这个对象的监视器时,竞争是不分先后的,系统随机
synchronized的另外两种写法
写法一(只是常规写法的对象固定为当前类对象):
public synchronized void test() {
}
等价于
public void test() {
synchronized(this) {
}
}
写法二(常规写法的对象固定为类.class):
```java
class Test{
public synchronized static void test() {
}
}
等价于
public static void test() {
synchronized(Test.class) {
}
}
volatile 易变的
用于修饰成员变量/静态成员变量
优点 :
防止线程从自己的高速缓存中查找变量的值,必须到主存中获取它的值
局限性 :
只保证变量在多个线程间的可见性,不保证原子性
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(run){
}
});
t.start();
Thread.sleep(1000);
run = false;//对t线程不可见(导致线程无法停止),除非在定义run时添加volatile修饰符
}
相比之下,synchronized 语句块既可以保证代码块的原子性,也可以保证代码块内变量的可见性。但缺点是synchronized是属于重量级操作,性能会受到影响。
线程死锁
一般来说,成为owner可以理解为获得了对象的锁,死锁即是:
a线程获得A对象的锁,锁里还获取B对象的锁
b线程获得B对象的锁,锁里还获得A对象的锁
处理方法 : 尽可能避免
wait(),notify(),notifyAll()
前提 : 首先获得对象锁,否则无法使用wait,notify,notifyAll
特点 : 都属于Object对象的方法
Object obj = new Object();
obj.wait(); 让object监视器的线程等待
obj.notify(); 让object上正在等待的线程中挑一个唤醒
obj.notifyAll(); 让object上正在等待的线程全部唤醒
Object obj = new Object();
new Thread(()-> {
synchronized (obj) {
System.out.println("thread-0线程执行....");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread-0其它代码....");
}
}).start();
new Thread(()-> {
synchronized (obj) {
System.out.println("thread-1线程执行....");
try {
obj.wait(); // 让线程在obj上一直等待下去
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread-1其它代码....");
}
}).start();
System.out.println("唤醒obj上其它线程");
synchronized (obj){
// obj.notify();
obj.notifyAll();
}
wait(long n) 有时限的等待, 到n毫秒后结束等待,或是被notify
sleep(long n) , wait(long n) 的区别
1) sleep是Thread的方法,wait是Object的方法
2) wait使用的前提是获得对象锁(使用了synchronized)
3) sleep睡眠时不会释放对象锁,wait会释放
线程的状态
NEW (新建): 刚创建了线程,未start
RUANNABLE(可运行的): start之后
BLOCKED(阻塞) : 线程进入monitor监视区,然后在entrySet竞争owner位置
WAIT类型 :
1)WAITING(等待) : 调用了wait或者join方法进入WaitSet,处于等待状态
2)TIMED_WAITING 当调用wait(long n) join(long n) 进入了WaitSet,处于有限时的等待状态
TERMINATED (终止)当线程代码运行结束