系统学习javaweb-04-进程
Write By CS逍遥剑仙
我的主页: csxiaoyao.com
GitHub: github.com/csxiaoyaojianxian
Email: sunjianfeng@csxiaoyao.com
QQ: 1724338257
1 进程概念
线程:线程在一个进程中负责代码的执行,是进程中一个执行路径
多线程:在一个进程中有多个线程同时在执行不同的任务
一个java应用程序至少有两个线程,一个是主线程负责main方法代码的执行,一个是垃圾回收器线程,负责了回收垃圾。
2 创建进程方式一
2.1 方式一步骤
创建线程的两个方式:
【方式一】:
- 自定义一个类继承Thread类
. 重写Thread类的run方法 , 把自定义线程的任务代码写在run方法中- 创建Thread的子类对象,并且调用start方法开启线程
注意:一个线程一旦开启,那么线程就会执行run方法中的代码,run方法千万不能直接调用,直接调用run方法就相当调用了一个普通的方法而已,并没有开启新的线程
public class test extends Thread { @Override //把自定义线程的任务代码写在run方法中。 public void run() { for(int i = 0 ; i < 100 ; i++){ System.out.println("自定义线程:"+i); } } public static void main(String[] args) { //创建了自定义的线程对象。 test thread = new test(); //调用start方法启动线程 thread.start(); for(int i = 0 ; i < 100 ; i++){ System.out.println("main线程:"+i); } } }
class TalkThread extends Thread{ @Override public void run() { while(true){ System.out.println("语音聊天"); } } } class VideoThread extends Thread{ @Override public void run() { while(true){ System.out.println("播放视频"); } } } public class test { public static void main(String[] args) { // 语音聊天与视频聊天 TalkThread talkThread = new TalkThread(); talkThread.start(); VideoThread videoThread = new VideoThread(); videoThread.start(); } }
2.2 常用方法
方法
—-Thread(String name) 初始化线程的名字
—-setName(String name) 设置线程对象名
—-getName() 返回线程的名字
—-sleep() 线程睡眠指定的毫秒数( 静态方法)
—-currentThread() 返回当前的线程对象(静态方法)
—-getPriority() 返回当前线程对象的优先级,默认线程的优先级是5
—-setPriority(int newPriority) 设置线程的优先级,虽然设置了线程的优先级,但是具体的实现取决于底层的操作系统的实现(最大的优先级是10,最小的1,默认是5)
public class test extends Thread { public test(String name){ super(name); //调用了Thread类的一个参数的构造方法。 } @Override public void run() { System.out.println("this:"+ this); System.out.println("当前线程对象:" + Thread.currentThread()); for (int i = 0; i < 30 ; i++) { //sunshine:x System.out.println(Thread.currentThread().getName()+":"+i); //Thread类的run方法没有抛出异常类型,所以子类不能抛出异常类型,只能捕获 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { //创建了一个线程对象 test d = new test("sunshine"); //设置线程的优先级, 优先级的数字越大,优先级越高,优先级的范围是1~10 d.setPriority(10); d.start(); for (int i = 0; i < 30 ; i++) { // main:x System.out.println(Thread.currentThread().getName()+":"+i); } System.out.println("自定义线程的优先级:"+d.getPriority()); //线程的优先级默认是5 System.out.println("主线程的优先级:"+Thread.currentThread().getPriority()); Thread mainThread = Thread.currentThread(); System.out.println("主线程的名字:"+ mainThread.getName()); } }
3 创建进程方式二(推荐)
3.1 方式二步骤
【方式二】(推荐):
- 自定义一个类实现Runnable接口
- 实现Runnable接口的run方法,把自定义线程的任务定义在run方法上
- 创建Runnable实现类对象
- 创建Thread类的对象,并且把Runnable实现类的对象作为实参传递
- 调用Thread对象的start方法开启一个线程
注意:Runnable实现类的对象并不是一个线程对象,只是实现了Runnable接口的对象而已。只有是Thread或者是Thread的子类才是线程对象。把Runnable实现类的对象作为实参传递给Thread对象,作用就是把Runnable实现类的对象的run方法作为了线程的任务代码去执行了。
推荐使用第二种实现Runnable接口的方法,因为java单继承,多实现的。
public class test implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } public static void main(String[] args) { // 创建Runnable实现类的对象 test d = new test(); // 创建Thread类的对象, 把Runnable实现类对象作为实参传递 Thread thread1 = new Thread(d, "sunshine1"); // Thread类使用Target变量记录了d对象 Thread thread2 = new Thread(d, "sunshine2"); // Thread类使用Target变量记录了d对象 Thread thread3 = new Thread(d, "sunshine3"); // Thread类使用Target变量记录了d对象, // 调用thread对象的start方法开启线程 thread1.start(); thread2.start(); thread3.start(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
4 线程安全问题
sun提供了线程同步机制
4.1 方式一:同步代码块
synchronized(锁对象){ 需要被同步的代码... }
注意事项:
- 任意的一个对象都可以做为锁对象
- 在同步代码块中调用sleep方法并不是释放锁对象的
- 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率
- 多线程操作的锁对象必须是唯一共享的,否则无效
4.2 方式二:同步函数
同步函数就是使用synchronized修饰一个函数。
注意事项 :
- 如果是一个非静态的同步函数的锁对象是this对象,如果是静态的同步函数的锁 对象是当前函数所属的类的字节码文件(class对象)。
- 同步函数的锁对象是固定的,不能指定。
4.3 推荐使用同步代码块
原因:
- 同步代码块的锁对象可以随意指定,方便控制。同步函数的锁对象是固定的,不能指定。
- 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数 的所有代码都被同步了。
class SaleTicket extends Thread{ //票数,静态成员变量 static int num = 50; // static Object o = new Object(); public SaleTicket(String name) { super(name); } @Override public void run() { while(true){ //同步代码块 synchronized ("锁") { if(num>0){ System.out.println(Thread.currentThread().getName()+"售出了第"+num+"号票"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } num--; }else{ System.out.println("售罄了.."); break; } } } } } public class test { public static void main(String[] args) { //创建三个线程对象,模拟三个窗口 SaleTicket thread1 = new SaleTicket("窗口1"); SaleTicket thread2 = new SaleTicket("窗口2"); SaleTicket thread3 = new SaleTicket("窗口3"); //开启线程售票 thread1.start(); thread2.start(); thread3.start(); } }
4.4 死锁现象出现的根本原因
原因:
- 存在两个或者两个以上的线程
- 存在两个或者两个以上的共享资源
解决方案:尽量避免发生
5 线程通讯
一个线程完成任务时,要通知另一个线程去完成另一个任务
【生产者与消费者问题】
—-wait(): 等待,如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。
—-notify(): 唤醒,唤醒线程池等待线程其中的一个。
—-notifyAll(): 唤醒线程池所有等待线程。
wait与notify方法要注意的事项:
- wait方法与notify方法属于Object对象。
- wait方法与notify方法必须要在同步代码块或者是同步函数中才能使用。
- wait方法与notify方法必需要由锁对象调用。
//产品类 class Product { String name; // 名字 double price; // 价格 boolean flag = false; // 产品是否生产完毕的标识,默认情况是没有生产完成。 } // 生产者 class Producer extends Thread { Product p; // 产品 public Producer(Product p) { this.p = p; } @Override public void run() { int i = 0; while (true) { synchronized (p) { if (p.flag == false) { if (i % 2 == 0) { p.name = "苹果"; p.price = 6.5; } else { p.name = "香蕉"; p.price = 2.0; } System.out.println("生产者生产出了:" + p.name + " 价格是:" + p.price); p.flag = true; i++; p.notifyAll(); // 唤醒消费者去消费 } else { // 已经生产 完毕,等待消费者先去消费 try { p.wait(); // 生产者等待 } catch (InterruptedException e) { e.printStackTrace(); } } } } } } // 消费者 class Customer extends Thread { Product p; public Customer(Product p) { this.p = p; } @Override public void run() { while (true) { synchronized (p) { if (p.flag == true) { // 产品已经生产完毕 System.out.println("消费者消费了" + p.name + " 价格:" + p.price); p.flag = false; p.notifyAll(); // 唤醒生产者去生产 } else { // 产品还没有生产,应该 等待生产者先生产。 try { p.wait(); // 消费者也等待了... } catch (InterruptedException e) { e.printStackTrace(); } } } } } } public class test { public static void main(String[] args) { Product p = new Product(); // 产品 // 创建生产对象 Producer producer = new Producer(p); // 创建消费者 Customer customer = new Customer(p); // 调用start方法开启线程 producer.start(); customer.start(); } }
6 线程的停止
- 停止一个线程,一般会通过一个变量去控制。
- 如果需要停止一个处于等待状态的线程,需要通过变量配合notify方法或者interrupt()来使用。
public class test extends Thread { boolean flag = true;//标识符 public test(String name) { super(name); } //同步函数 @Override public synchronized void run() { int i = 0; while (flag) { try { this.wait(); // 等待.. } catch (InterruptedException e) { System.out.println("出现异常.."); } System.out.println(Thread.currentThread().getName() + ":" + i); i++; } } public static void main(String[] args) { test d = new test("sunshine"); d.setPriority(10); d.start(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); // 当主线程的i是80的时候停止sunshine线程。 if (i == 80) { d.flag = false; // 方法一:把线程的等待状态强制清除,被清除状态的线程会接收到一个InterruptedException,停止 d.interrupt(); // 方法二:修改标志位,并唤醒进程 // synchronized (d) { // d.notify(); //唤醒后状态位为false,停止 // } } } } }
8 守护线程(后台线程)
在一个进程中如果只剩下了守护线程,那么守护线程也会死亡。
一个线程默认都不是守护线程。
//模拟下载更新包 public class test extends Thread { public test(String name){ super(name); } @Override public void run() { for(int i = 1 ; i<=100 ; i++){ System.out.println("更新包目前下载"+i+"%"); if(i==100){ System.out.println("更新包下载完毕,准备安装.."); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { test d = new test("后台线程"); d.setDaemon(true); // setDaemon() 设置线程是否为守护线程,true为守护线程, false为非守护线程。 System.out.println("是守护线程吗?" + d.isDaemon()); // 判断线程是否为守护线程。 d.start(); for (int i = 1; i <= 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
9 join方法
加入
class Mon extends Thread{ 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{ @Override public void run() { System.out.println("儿子下楼.."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("儿子一直往前走"); System.out.println("儿子打完酱油了"); System.out.println("上楼,把酱油给老妈"); } } public class test { public static void main(String[] args) { Mon m = new Mon(); m.start(); } }