线程的分类:
一种是守护线程,一种是用户线程
他们几乎在每个方面都是相同的,唯一的区别是判断JVM 何时离开
守护线程是用来服务用户线程的,通过在start() 方法前调用Thread.setDeamon(true) 可以把一个用户线程变成一个守护线程
JAVA的垃圾回收就是一个典型的守护线程
若JVM 中都是守护线程, 当前JVM 将退出
创建多线程的两种方法:继承Thread 类的方式 vs 实现Runnable 接口的方式
联系: public class Thread implements Runable
哪个方式好? 实现Runnable 接口的方式优于继承Thread 类的方式
避免了java 中单继承的局限性
如果多个线程要操作同一份资源(或者数据),更适合使用实现的方法, 因为实现的方式不需要创建多个对象。
通过集成的方式实现多线程:
class TicketSaleThread extends Thread{
private static int totalTickets = 100;
public void run(){
while(true){
if (totalTickets > 0){
System.out.println(Thread.currentThread().getName() + ": " + totalTickets--);
}else{
break;
}
}
}
}
public class TicketSale {
public static void main(String[] args){
TicketSaleThread tst1 = new TicketSaleThread();
tst1.setName("窗口一");
tst1.start();
TicketSaleThread tst2 = new TicketSaleThread();
tst2.setName("窗口二");
tst2.start();
TicketSaleThread tst3 = new TicketSaleThread();
tst3.setName("窗口三");
tst3.start();
}
}
通过实现Runnable 接口的方式实现多线程:
//1. 创建一个Runnable 的接口类
class TicketSaleThread implements Runnable {
private int totalTickets = 100;
//2. 实现接口的抽象方法
public void run() {
// 3. 子线程的执行代码
while (true) {
if (totalTickets > 0) {
System.out.println(Thread.currentThread().getName() + ": " + totalTickets--);
} else {
break;
}
}
}
}
public class TicketSale{
public static void main(String[] args){
// 4. 创建一个Runnable 实现类的对象
TicketSaleThread tt = new TicketSaleThread();
//5. 以上面创建的对象做为构造函数的参数,来创建Thread 类的对象,再调用Thread 类的start 方法
Thread tr1 = new Thread(tt);
tr1.setName("窗口一:");
tr1.start();
Thread tr2 = new Thread(tt);
tr2.setName("窗口二:");
tr2.start();
Thread tr3 = new Thread(tt);
tr3.setName("窗口三:");
tr3.start();
}
}
线程的各种状态:
线程的死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
解决办法:
专门的算法,原则
尽量减少同步资源的定义
线程的通信:
wait(): 令当前线程挂起并放弃CPU, 同步资源,使别的线程可以访问并修改共享资源,而当前线程排队等候再次对资源的访问
notify(): 唤醒 正在排队等待同步资源的线程中 优先级最高者结束等待
notifyAll(): 唤醒正在排队等待资源的所有线程结束等待
Object提供的这三个方法只有在synchronized方法或synchronized代码快中才能使用,否则会报 illegalMonitorStateException异常