Java04

程序、进程、线程

程序:是为了完成特定任务、用某种语言编写的一组指令的集合(也就是写的代码)
进程:进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程。
线程:1)线程由进程创建,是进程的一个实体 2)一个进程可以有多个线程
(比如,迅雷下载东西,启动迅雷就是一个进程,里面的每一个下载任务就是一个线程)

单线程:同一个时刻,只允许执行一个线程
多线程:同一时刻,可以执行多个线程(比如,一个qq进程,可以同时打开多个聊天窗口;一个迅雷进程,可以同时下载多个文件)

并发、并行

并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单地说,单核CPU实现的多任务就是并发
并行:同一个时刻,多个任务同时执行。多核CPU可以实现并行

  • 线程使用——实现Runnable接口
    Java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时再用继承Thread类方法来创建线程是不可能的,因此Java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程。
package thread_;

/**
 * 编写程序,创建两个线程,一个线程每隔1秒输出“hello,world”,输出10次,退出
 * 一个线程每隔1秒输出“hi”,输出5次退出
 */
public class Thread03 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread = new Thread(t1);
        Thread thread1 = new Thread(t2);
        thread1.start();
        thread.start();
    }
}

class T1 implements Runnable{
    int count = 0;
    @Override
    public void run() {
        while (true){
            System.out.println("hello,world" +  (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10)
                break;
        }
    }
}

class T2 implements Runnable{
    int count = 0;
    @Override
    public void run() {
        while (true){
            System.out.println("hi" +  (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 5)
                break;
        }
    }
}

主要看下两种方式实现多线程

package thread_;

/**
 * 使用多线程模拟三个窗口同时售票
 * 主要看下两种方式实现多线程
 */
public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//
//        SellTicket01 sellTicket02 = new SellTicket01();
//
//        SellTicket01 sellTicket03 = new SellTicket01();
//        // 这里会出现票数超卖
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

        System.out.println("==========使用实现接口的方式来售票==============");
        SellTicket02 sellTicket02 = new SellTicket02();
        new Thread(sellTicket02).start();   // 第1个线程-窗口
        new Thread(sellTicket02).start();   // 第2个线程-窗口
        new Thread(sellTicket02).start();   // 第3个线程-窗口


    }
}


// 使用Thread方式
class SellTicket01 extends Thread{
    private static int ticketNum = 100; // 让多个线程共享 ticketNum

    @Override
    public void run() {
        while (true){
            if (ticketNum <= 0){
                System.out.println("售票结束");
                break;
            }

            // 休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票 " + "剩余票数" + (--ticketNum));
        }


    }
}

// 实现接口的方式
class SellTicket02 implements Runnable{

    private int ticketNum = 100; // 让多个线程共享 ticketNum

    @Override
    public void run() {
        while (true){
            if (ticketNum <= 0){
                System.out.println("售票结束");
                break;
            }

            // 休眠50毫秒
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票 " + "剩余票数" + (--ticketNum));
        }
        
    }
}
// 上面也会出现票超卖的情况

通知线程退出

线程终止:

  1. 当线程完成任务后,会自动退出
  2. 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
  • interrupt,中断线程,但并没有真正的结束线程,所以一般用于中断正在休眠的线程
  • sleep,线程的静态方法,使当前线程休眠

线程插队

  • yield:线程的礼让。让出CPU,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
  • join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
package thread_;

public class ThreadMethodExercise extends Thread{

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new T4());
        for (int i = 1; i < 11; i++) {
            System.out.println("hi");
            if (i == 5){
                thread.start();     // 启动子线程
                thread.join();      // 立即将该子线程插入到main,让该子线程先执行
            }
        }
    }
}

class T4 implements Runnable{
    int count = 10;
    @Override
    public void run() {
        while (true){

            System.out.println("hello" + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10)
                break;
        }
    }
}

守护线程

  • 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  • 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  • 常见的守护线程:垃圾回收机制

线程同步机制

  • 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
  • 使用关键字 Synchronized
package thread_;

/**
 * 使用多线程模拟三个窗口同时售票
 * 主要看下两种方式实现多线程
 */
public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//
//        SellTicket01 sellTicket02 = new SellTicket01();
//
//        SellTicket01 sellTicket03 = new SellTicket01();
//        // 这里会出现票数超卖
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

//        System.out.println("==========使用实现接口的方式来售票==============");
//        SellTicket02 sellTicket02 = new SellTicket02();
//        new Thread(sellTicket02).start();   // 第1个线程-窗口
//        new Thread(sellTicket02).start();   // 第2个线程-窗口
//        new Thread(sellTicket02).start();   // 第3个线程-窗口


        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();   // 第1个线程-窗口
        new Thread(sellTicket03).start();   // 第2个线程-窗口
        new Thread(sellTicket03).start();   // 第3个线程-窗口

    }
}

// 使用synchronized解决同步问题
class SellTicket03 implements Runnable{

    private int ticketNum = 100; // 让多个线程共享 ticketNum

    private boolean loop = true;        // 控制run 方法的变量
    public synchronized void sell(){        // 同步方法 给sell方法上锁 在同一时刻,只能有一个线程来执行sell方法
        if (ticketNum <= 0){
            System.out.println("售票结束");
            loop = false;
            return;
        }

        // 休眠50毫秒
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票 " + "剩余票数" + (--ticketNum));
    }
    @Override
    public void run() {
        while (loop){
           sell();
        }

    }
}
  • Java语言中,引入对象互斥锁的概念,来保证共享数据操作的完整性

  • 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

  • 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问

  • 同步的局限性:导致程序的执行效率要降低

  • 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)

  • 同步方法(静态的)的锁为当前类本身 默认锁对象:当前类.class

  • 线程的死锁:多个线程都占用了对方的锁资源,但不肯相让,导致了死锁

释放锁

  • 线程执行同步代码块或同步方法时,程序调用Thread.sleep(), Thread.yield()方法暂停当前线程的执行,不会释放锁
  • 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁
  • 应尽量避免使用suspend() 和 resume()来控制线程
    在这里插入图片描述
    练习
package thread_;

import java.util.Scanner;

/**
 * 在main方法中启动两个线程,在第一个线程循环随机打印100以内的整数,直到第二个线程从键盘读取了“Q”命令
 */
public class Homework01 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B(a);     // 一定要把a传给线程b,不然呢没办法控制a
        a.start();
        b.start();
    }
}

class A extends Thread{
    private boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        while (loop){
            System.out.println((int) (Math.random() * 100 + 1));

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

class B extends Thread{
    private A a;
    Scanner scanner = new Scanner(System.in);
    public B(A a) {     // 构造器中直接传入A类对象
        this.a = a;
    }

    @Override
    public void run() {
        while (true){
            System.out.println("输入Q表示退出");
            char key = scanner.next().toUpperCase().charAt(0);
            if (key == 'Q'){
                // 以通知方式结束a线程
                a.setLoop(false);
                System.out.println("b线程退出");
                break;
            }
        }
    }
}

练习

package thread_;

public class Homework02 {
    public static void main(String[] args) {
        T t = new T();
        Thread thread = new Thread(t);
        thread.setName("t1");
        Thread thread1 = new Thread(t);
        thread1.setName("t2");
        thread.start();
        thread1.start();
    }
}

// 每次取1000元

// 因为这里涉及到多个线程共享资源,所以使用实现Runnable方法
class T implements Runnable {
    private int money = 10000;

    @Override
    public void run() {
        while (true) {
            // 1. 这里使用synchronized 实现了线程同步
            // 2. 当多个线程执行到这里时,就会去争夺this对象锁
            // 3. 哪个线程正道到this对象锁,就执行synchronized代码块,执行完就释放this对象锁
            // 4. 争夺不到this对象锁,就blocked,准备下次争夺
            synchronized (this){
                if (money < 1000) {
                    System.out.println("余额不足");
                    break;
                }
                money -= 1000;
                System.out.println(Thread.currentThread().getName() + " : " + money);
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>