多线程创建的两种方式
实现Runnable接口
package com.ckf.runnable;
class MyThread implements Runnable{
private int ticketsCont = 5; //一共五张火车票
@Override
public void run() {
while(ticketsCont > 0) {
ticketsCont--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,剩余票数为:"+ ticketsCont);
}
}
}
public class TicketsRunnable {
public static void main(String[] args) {
MyThread mt = new MyThread();
//创建三个线程来模拟三个售票窗口
Thread th1 = new Thread(mt,"窗口1");
Thread th2 = new Thread(mt,"窗口2");
Thread th3 = new Thread(mt,"窗口3");
//启动三个线程,开始卖票
th1.start();
th2.start();
th3.start();
}
}
继承Thread类
package com.ckf.thread;
class MyThread extends Thread{
private int ticketsCont = 5; //一共五张火车票
private String name; //窗口,即是线程的名字
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
while(ticketsCont > 0) {
ticketsCont--;
System.out.println(name + "卖了一张票,剩余票数为:"+ ticketsCont);
}
}
}
public class TicketsThread {
public static void main(String[] args) {
//创建三个线程,模拟窗口卖票
MyThread mt1 = new MyThread("窗口1");
MyThread mt2 = new MyThread("窗口2");
MyThread mt3 = new MyThread("窗口3");
//启动这三个线程,开始卖票
mt1.start();
mt2.start();
mt3.start();
}
两种方式的区别
- Runnable 方式可以避免Thread方式由于Java单继承特性带来的缺陷
- Runnable 的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况
线程的生命周期
- 创建状态:编写线程代码
- 就绪状态:调用线程的start()方法,此时线程只是进入等待队列,等待CPU的调用,并不是直接开始运行。
- 运行状态:获取CPU使用权,开始执行代码逻辑
- 阻塞状态:处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。如调用了sleep()方法,wait()方法等。
- 终止状态:线程执行完成或因异常退出,结束线程生命周期。
如何停止线程
使用stop() 方法
如何中断线程
调用interrupt()方法只是打了个中断标志,并非真正的退出,退出逻辑需要自己写。
package com.ckf.runnable
public class InterruptDemo {
static class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <100000 ; i++) {
System.out.println("i="+(i+1));
}
}
}
public static void main(String[] args) {
MyThread mt=new MyThread();
Thread thread=new Thread(mt);
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
}
notify、join、yield的方法说明
notify/nptifyAll:唤醒waiting态的线程,notify和notifyAll的区别在于前者只能唤醒monitor上的一个线程,对其他线程没有影响,而notifyAll则唤醒所有的线程。
join:当线程调用另外一个线程的join方法时,当前线程就会进入阻塞状态。直到另外一个线程执行完毕,当前线程才会由阻塞状态转为就绪状态。
yield:暂停当前正在执行的线程对象(及放弃当前拥有的cup资源),并执行其他线程。yield()做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。
线程的异常处理
线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked exception处理掉。一个异常被抛出后,如果没有被捕获处理,则会一直向上抛。异常一旦被Thread.run() 抛出后,就不能在程序中对异常进行捕获,最终只能由JVM捕获。
class MyThread extends Thread{
public void run(){
System.out.println("Throwing in " +"MyThread");
throw new RuntimeException();
}
}
class Main {
public static void main(String[] args){
MyThread t = new MyThread();
t.start();
try{
Thread.sleep(1000);
}
catch (Exception x){
System.out.println("Caught it" + x);
}
System.out.println("Exiting main");
}
}
线程锁死问题
两个或多个线程互相持有对方需要的锁而导致这些线程全部处于永久阻塞状态,不会出现提示。
例如:
public void run() {
synchronized (instance1) {
System.out.println("我是对象锁的代码块形式。我叫" + Thread.currentThread().getName());
synchronized (instance2){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
synchronized (instance2) {
System.out.println("我是对象锁的代码块形式。我叫" + Thread.currentThread().getName());
synchronized (instance1){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
类似一个持有1的情况下请求2,另一个有2的情况下请求1
解决锁死的办法
减少锁的嵌套