后端

系统学习javaweb-04-进程

csxiaoyao · 1月23日 · 2017年 本文7652字 · 阅读20分钟60

系统学习javaweb-04-进程

Write By CS逍遥剑仙
我的主页: csxiaoyao.com
GitHub: github.com/csxiaoyaojianxian
Email: sunjianfeng@csxiaoyao.com
QQ: 1724338257

1 进程概念

线程:线程在一个进程中负责代码的执行,是进程中一个执行路径
多线程:在一个进程中有多个线程同时在执行不同的任务
一个java应用程序至少有两个线程,一个是主线程负责main方法代码的执行,一个是垃圾回收器线程,负责了回收垃圾。

2 创建进程方式一

2.1 方式一步骤

创建线程的两个方式:

【方式一】:

  1. 自定义一个类继承Thread类
    . 重写Thread类的run方法 , 把自定义线程的任务代码写在run方法中
  2. 创建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 方式二步骤

【方式二】(推荐):

  1. 自定义一个类实现Runnable接口
  2. 实现Runnable接口的run方法,把自定义线程的任务定义在run方法上
  3. 创建Runnable实现类对象
  4. 创建Thread类的对象,并且把Runnable实现类的对象作为实参传递
  5. 调用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(锁对象){
    需要被同步的代码...
}

注意事项:

  1. 任意的一个对象都可以做为锁对象
  2. 在同步代码块中调用sleep方法并不是释放锁对象的
  3. 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率
  4. 多线程操作的锁对象必须是唯一共享的,否则无效

4.2 方式二:同步函数

同步函数就是使用synchronized修饰一个函数。

注意事项 :

  1. 如果是一个非静态的同步函数的锁对象是this对象,如果是静态的同步函数的锁 对象是当前函数所属的类的字节码文件(class对象)。
  2. 同步函数的锁对象是固定的,不能指定。

4.3 推荐使用同步代码块

原因:

  1. 同步代码块的锁对象可以随意指定,方便控制。同步函数的锁对象是固定的,不能指定。
  2. 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数 的所有代码都被同步了。
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 死锁现象出现的根本原因

原因:

  1. 存在两个或者两个以上的线程
  2. 存在两个或者两个以上的共享资源

解决方案:尽量避免发生

5 线程通讯

一个线程完成任务时,要通知另一个线程去完成另一个任务

【生产者与消费者问题】
—-wait(): 等待,如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify方法才能唤醒。
—-notify(): 唤醒,唤醒线程池等待线程其中的一个。
—-notifyAll(): 唤醒线程池所有等待线程。

wait与notify方法要注意的事项:

  1. wait方法与notify方法属于Object对象。
  2. wait方法与notify方法必须要在同步代码块或者是同步函数中才能使用。
  3. 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 线程的停止

  1. 停止一个线程,一般会通过一个变量去控制。
  2. 如果需要停止一个处于等待状态的线程,需要通过变量配合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();
    }
}

系统学习javaweb-04-进程-禅林阆苑

0 条回应

×