Java线程
运行的一个java程序会产生一个进程,一个java进程对应一个jvm实例,每个线程可以共享JVM里的堆,java是单线程编程模型
Thread的start和run的区别
start方法会创建一个新的子线程并启动
run方法只是Thread的一个普通调用
就是start会创建一个新线程,而run并不会创建一个新的线程
Thread和Runnable的区别
最明显,thread是一个类。而runnable是一个接口。
thread通过提供一个参数为Runnable的有参构造,使其鞥实现多线程,由于单一继承原则,推荐使用runnable接口
处理线程返回值
- 主线程等待法
对要获取的变量进行循环等待,当变量为需要的值时跳出循环。 - 使用threa类的join()阻塞当前线程,让子线程处理完毕
- 通过Callable接口实现,通过FutureTask或者线程池实现
//通过Callable接口实现
public class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
String value = "test";
System.out.println("ready to work");
Thread.currentThread().sleep(5000);
System.out.println("done");
return value;
}
}
public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<String>(new MyCallable());
new Thread(task).start();
if(!task.isDone()) {
System.out.println("not finished");
}
System.out.println(task.get());
}
}
thread中存在含有存在runnable参数的构造函数,没有callable接口的构造函数,java提供了FutureTask类,它实现了RunnableFuture接口,而该接口继承了Runnable, Future类,FutureTask中有callable为参数的构造,因此使用FutureTask使集合了runnable和callable
FutureTask构造函数中有callable为参数
FutureTask实现了RunnableFuture接口
RunnableFuture接口继承了Runnable, Future类
通过线程池获取
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadpool = Executors.newCachedThreadPool();
Future<String> future = threadpool.submit(new MyCallable());
if (!future.isDone()) {
System.out.println("wait");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
threadpool.shutdown();
}
}
}
}
线程状态
共有六个状态
- 新建di’a
- 运行:包含正在运行和等待运行
- 无限期等待:不会分配时间执行,需要显示进行唤醒(可使用notify或者notifyAll)
- 限期等待:可以自动唤醒,由系统自动唤醒
- 阻塞:获取排他锁
- 结束:线程终止
在终止的线程上 调用start方法会报illegalStateException异常
sleep和wait的区别
- sleep是Thread类的方法,wait是Object类的方法
- sleep可以在任何时候调用
- wait只能在synchronized方法或者synchronized块中使用
- sleep只会让出cpu但是不会放弃锁
- wait会让出cpu并且释放占有的同步资源锁
//验证第五和第六
public class WaitSleepDemo {
public static void main(String[] args) throws InterruptedException {
final Object lock = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread is waittong");
synchronized (lock){
System.out.println("get lock");
try {
Thread.sleep(20);
System.out.println("thread wait");
lock.wait(1000);
System.out.println("thread done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Thread.sleep(10);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threadb is waittong");
synchronized (lock){
System.out.println("getb lock");
try {
System.out.println("threadb is sleeping");
Thread.sleep(20);
System.out.println("threadb done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
第一个线程采用的的wait,会释放获取的锁哦和cpu,因此第二个线程获取了锁,由于第二个线程使用的是sleep所以只释放cpu等到醒来后继续执行完第二个线程,期间不释放锁。当第二个线程结束时释放锁。此时第一个线程才能执行,最后第一个线程执行完毕。运行结果如下
notify¬ifyAll
锁池 EntryList
当线程A已经获得了某个对象的锁。其他线程速想要调用这个对象的synchronized方法或块时必须要获取该对象的锁,但该锁已被线程A占用,此时其他线程会进入该对象的锁池
等待池 WaitSet
线程A调用了对象的wait方法,就会释放该对象的锁,同时线程A进入对象的等待池中,等待池中的线程不会竞争该对象的锁
notifyAll让所有在等待池中的线程进入锁池获取竞争锁的能力
notify随机选取一个在等待池中的线程进入锁池
yield
当调用yield函数,表示当前 线程愿意让出cpu
调度,但有此时线程调度器选择是否继续进行,不会对锁进行影响(就是说你愿意让出cpu是你的事,但是我是否同意你让出cpu是我的事)
interrupt
如何中断线程呢?
- 调用stop方法停止线程(不用)
- 调用suspend和resume (不用)
- 调用interrupt
调用interrupt方法,如此时线程被阻塞,线程会立即退出阻塞状态进入活跃状态,并且抛出InterruprtedException异常,如果线程为活跃状态,就修改中断标志为true。此时线程正常运行。