一、进程、线程、多线程的定义:
进程、线程、多线程的定义:
进程:正在运行的程序,负责了这个程序的内存空间分配,代表了内存中的执行区域。 (一个正在运行的QQ应用程序) 线程:就是在进程中负责一个执行路径。 (QQ应用程序的视频、聊天、语音) 多线程:就是一个进程中多个执行路径同时执行 (同时使用QQ的视频和打字功能)
面试题:一个java程序执行过程中必备的几个线程? 两个 1、主线程 2、垃圾回收器线程 |
二、创建线程的步骤:
创建线程的步骤: 1、继承Thread类(方式一)java.lang.Thread 2、重写run方法 3、创建该线程类对象 4、调用start()方法。 |
//1、继承Thread类 public class Demo2 extends Thread{ //2、重写run方法 @Override public void run() {
for(int i=0; i<100; i++) { System.out.println("Demo2"+":"+i); } } public static void main(String[] args) { //3、创建该线程类对象 Demo2 demo2 = new Demo2(); //4、调用start()方法。 demo2.start();
for(int i=0; i<100; i++) { System.out.println("main"+":"+i); } } } |
三、线程的生命周期:
四、线程的安全问题:
引出线程安全的根本原因: 1、存在两个或两个以上的线程 2、多个线程操作了共享资源。
解决方案: 1、使用同步代码块。 synchronized(锁对象) { 同步代码区域; } 2、使用同步函数 a.非静态同步函数的锁对象是this对象,静态函数对象是当所属类的class文件对象。 b.同步函数的锁对象是固定的,无法更改。同步函数锁的范围更大,并不是只是锁住共享的代码块。 所以推荐用同步代码块的方式。 使用同步时候需要注意的问题: 1、锁对象可以是任意的对象(必须得是共享对象) 2、锁对象必须是多个线程 3、线程调用了sleep方法是不会被释放锁对象的。 4、只有会遇到线程安全问题时候,才会使用锁机制。否则会影响执行效率问题。 |
五、线程的安全需求案例:
案例一、模拟车站卖票过程
class SaleTickets extends Thread { static int num = 50; public SaleTickets(String name) { super(name); }
public void run() { while (true) { synchronized ("锁") { if (num > 0) { System.out.println(Thread.currentThread().getName() + "卖了第 " + num + " 张票"); num--; } else { System.out.println("售罄了!"); break; } } } } } public class Demo5 {
public static void main(String[] args) { SaleTickets st1 = new SaleTickets("窗口一"); SaleTickets st2 = new SaleTickets("窗口二"); SaleTickets st3 = new SaleTickets("窗口三");
st1.start(); st2.start(); st3.start(); } } |
案例二、夫妻俩去银行取钱
class DrawMoney extends Thread {
static int money = 5000;
public DrawMoney(String name) { super(name); }
public void run() { while (true) { synchronized ("锁") { if (money > 0) { System.out.println(Thread.currentThread().getName() + "取了100元," + "还剩下" + money); money -= 100; } else { System.out.println("别败家了,没钱了!"); break; } } } }
} public class Demo6 {
public static void main(String[] args) {
DrawMoney dt1 = new DrawMoney("丈夫"); DrawMoney dt2 = new DrawMoney("妻子");
dt1.start(); dt2.start(); }
} |
六、守护线程:
/** 守护线程:(后台线程) 当一个java应用只剩下守护线程的时候,那么守护线程马上结束。 */ |
class MyThread extends Thread {
public MyThread(String name) { super(name); }
@Override public void run() { for(int i=1; i<=100; i++) { System.out.println(this.getName()+"文件已经下载了"+i+"%"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("文件下载完毕"); } } public class Demo1 extends Thread{
public static void main(String[] args) {
MyThread mt = new MyThread("守护线程"); System.out.println(mt.isDaemon()); mt.start();
for(int i=1; i<=100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } |
七、线程让步:
/** 线程让步:(join关键字) 案例:妈妈让你打酱油。 */ |
class Mother extends Thread{
public Mother(String name) { super(name); }
@Override public void run() {
System.out.println("妈妈在洗菜"); System.out.println("妈妈在切菜"); System.out.println("妈妈在发现没酱油了,让儿子去打酱油。"); //这个时候儿子应该去打酱油 Son s = new Son("儿子"); s.start(); try { s.join();//这句话的意思不是儿子调用join,而是妈妈这个线程指定这个时刻先执行儿子线程。 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("妈妈在炒菜"); System.out.println("全家人坐在一起吃饭。"); } } class Son extends Thread {
public Son(String name) { super(name); }
@Override public void run() {
System.out.println("儿子走出家门"); System.out.println("儿子打酱油路上"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("儿子打酱油归来"); } } public class Demo2 {
public static void main(String[] args) {
Mother m = new Mother("妈妈");
m.start(); } } |
八、线程通讯问题:
/** 线程通讯问题:(重点以及难点) 当线程完成了一个任务的时候,需要通知另外一个线程去做其他事情。
wait():执行wait方法,线程会让该线程进入以锁对象建立的线程池中等待。 notify():执行notify方法,线程会唤醒以锁对象建立的线程池中等待的线程中的一个。 notifyAll():把所有的线程都唤醒。 */ |
class Product {
String name; int price;
boolean flag = false; //是否已经有产品生产 } class Producer extends Thread {
int i = 0; Product p;
public Producer(Product p) { this.p = p; } @Override public void run() { while(true) { synchronized (p) { if(!p.flag) { if(i%2==0) { p.name = "摩托车"; p.price = 3500; System.out.println("生产者生产了:"+p.name+",价格:"+p.price); }else{ p.name = "电动车"; p.price = 1500; System.out.println("生产者生产了:"+p.name+",价格:"+p.price); } i++; p.flag = true; //有产品已经被生产; p.notify(); //唤醒等待的消费线程。 }else{ try { p.wait(); //这个时候原本已经有产品被生产,所以该线程要进入等待。 } catch (InterruptedException e) { e.printStackTrace(); } } } } }
} class Consumer extends Thread {
Product p;
public Consumer(Product p) { this.p = p; }
@Override public void run() {
while(true) { synchronized (p) { if(p.flag) { System.out.println("消费者消费了价格为"+p.price+"的"+p.name); p.flag = false; p.notify();//唤醒等待的生产线程 }else { try { p.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
} public class Demo3 {
public static void main(String[] args) {
Product p = new Product(); Producer producer = new Producer(p); Consumer consumer = new Consumer(p);
producer.start(); consumer.start(); }
} |
九、停止线程:
/** 停止线程: 1.我们停止一个线程一般会配合你一个变量去控制。 2、如果我们停止的是一个等待状态下的线程,那么需要配合interrupt方法去使用. */ |
public class Demo4 extends Thread{
boolean flag = true; int i = 0;
public Demo4(String name) { super(name); } @Override public synchronized void run() { while(flag) { try { this.wait(); } catch (InterruptedException e) { System.out.println("抛出了一个异常:"+e); } System.out.println(Thread.currentThread().getName()+":"+i); i++; } }
public static void main(String[] args) {
Demo4 d = new Demo4("Genuine"); d.start();
// for(int i=0; i<100; i++) { // //当i为80的时候我想要停止这个自定义线程。 // if(i == 80) { // d.flag = false; //方式一 // } // System.out.println(Thread.currentThread().getName()+":"+i); // } for(int i=0; i<100; i++) { //当i为80的时候我想要停止这个自定义线程。 if(i == 80) { d.interrupt(); //中断一个在等待的线程状态。并且抛出一个interrupt异常。 } System.out.println(Thread.currentThread().getName()+":"+i); } }
} |