JAVA 多线程
- 线程的分类:
- 一种是守护线程,一种是用户线程
- 他们几乎在每个方面都是相同的,唯一的区别是判断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异常