初探
一个任务程为进程(浏览器,word),包含的多个子任务为线程(打字,查错同时)。常用的Windows、Linux等操作系统都采用抢占式多任务,如何调度线程完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
线程创建与运行
public class Main {
public static void main(String[] args) {
Thread t = new MyThread();//通过扩展Thread
Thread t = new Thread(new MyRunnable());//通过实现Runnable
Thread t = new Thread(() -> {
System.out.println("start new thread!");
});//通过lombda表达式
//简单的三种创建方式
t.start(); // 启动新线程
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("start new thread!");
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("start new thread!");
}
}
注意,这里的MyRunnable是Runnable的实现,仍需封装在Thread中!
问题来了,我们知道,我们是通过start()来启动线程,那么start是如何调用run()方法的呢?
public synchronized void start() {
......
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
查看源码可以发现,start()方法并没有直接调用run()方法,而是在其中运行了start0()的本地方法。
根据官方解释,start0()指示操作系统(通过JVM)创建一个新的线程,并让它执行run()方法。
由此可知,直接运行run()方法并不能实现多线程。
线程状态:
- New:新创建的线程,但未执行;
- Runnable:运行中的线程,正在执行
run()
方法; - Blocked:线程运行时,因某些操作被阻塞而挂起;//被动
- Waiting:线程运行时,因某些操作而在等待中;//主动
- Terminated:线程已终止,因为
run()
方法执行完毕。
Thread常用方法:
setPriority()设置该线程优先级(1最低,10最高,默认5)
Thread.sleep(long millis)当前线程睡眠(阻塞状态),单位毫秒,这是一个静态方法
join()加入线程,当前线程阻塞,直到该线程加入后再运行(在线程a中调用b.join(),a线程等待b加入后再运行)
interrupt()中断该线程,向目标线程发出中断请求,需线程通过isInterrupted()方法轮询并响应。与设置volatile标志位基本同理
setDaemon()设置该线程是否为守护线程,JVM在所有线程结束后关闭除了守护现成(通常用来关闭其他线程)
新的解决方案,从线程同步开始:
同步
因为,多个线程同时运行时,线程的调度由操作系统决定,程序自身无法决定。
所以,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。
这通常会导致读写时数据不一致问题,因为常见的i++等操作非原子操作(load,add,store)
为解决这个问题,我们可以用synchronized关键字对一个对象进行加锁
class Counter {
public static final Object lock = new Object();
public static int count = 0;
}
class AddThread extends Thread {
@Override
public void run() {
synchronized(Counter.lock) {//通过lock 对象进行加锁
Counter.count += 1;
}
}
}
//也可以这么写
public synchronized void add(int n) {//对this对象进行加锁(静态方法锁该类JVM创建的Class实例)
count += n;
}
现在问题又来了,我们知道synchronized 锁的是对象,那么它是如何实现的呢?
答案是锁对象头,一个对象通常由对象头,实例变量和填充数据组成。详见:
wait()与notify()
锁解决了线程的竞争,但仍存在协调问题,