第16章 线程

  • 程序:是为完成特定任务,用某种语言编写的一组指令的集合。简单的说,就是我们写的代码

  • 并发:同一个时刻,多个任务交替执行,造成一种貌似同事的错觉,单核cpu实现的多任务就是并发

  • 并行:多个任务同事执行,多核cpu可以实现并行

  • 小练习:检测本机cpu是多少核:

public class A{
    public static void main(String[] args){
        Runtime runtime = Runtime.getRuntime();
        int cpuNums = runtime.availableProcessors();
        System.out.println(cpuNums);
    }
}

创建线程的两种方法

(1)继承Thread类,重写run方法

(2)实现Runnable接口,重写run方法

  • 小练习:每隔一秒输出“你真好看”,总共输出4次结束

public class A{
    public static void main(String[] args){
        Cat cat = new Cat();
        cat.start();//不能使用run()方法
    }
}
//run Thread 类实现了Runnable 接口的run方法
class Cat extends Thread{
    @Override
    public void run() {//重写run方法
        int time = 0;
        //每隔一秒输出 你可真好看
        while (true) {
            System.out.println("你可真好看"+(++time));
            //休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(time > 4){
                break;
            }
        }
    }
}
  • 实现Runnable接口:

(1)java是单继承的,在某些情况下,一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然是不可能的

(2)于是,就有了实现Runnable接口这个方法来创建进程

  • 小例子:

    基本写法同上,只是在调用的时候有些不同

public class A{
    public static void main(String[] args){
        Cat cat = new Cat();
        Thread thread = new Thread(cat);
        thread.start();
    }
}
//run Thread 类实现了Runnable 接口的run方法
class Cat implements Runnable{
    @Override
    public void run() {//重写run方法
        int time = 0;
        //每隔一秒输出 你可真好看
        while (true) {
            System.out.println("你可真好看"+(++time));
            //休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(time > 4){
                break;
            }
        }
    }
}
  • 继承Thread vs实现Runable接口

    (1)从java设计上来看,两者没有本质区别,因为本质都是靠 start()方法来进行线程

    (2)实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制

  • 小练习:模拟三个窗口的买票

public class A{
    public static void main(String[] args){
//        SellTicket sellTicket = new SellTicket();
//        SellTicket sellTicket2 = new SellTicket();
//        SellTicket sellTicket3 = new SellTicket();
//
//        sellTicket.start();
//        sellTicket2.start();
//        sellTicket3.start();
        SellTicket2 sellTicket2 = new SellTicket2();
        new Thread(sellTicket2).start();
        new Thread(sellTicket2).start();
        new Thread(sellTicket2).start();

    }
}
class SellTicket extends Thread{
    public static int tickets= 100;

    @Override
    public void run() {
        while (true) {
            if (tickets < 0) {
                System.out.println("售票结束...");
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("正在售票。。。" + Thread.currentThread().getName() +
                    "还剩" + (--tickets) + "张票");

        }
    }
}
class SellTicket2 implements Runnable{
    public static int tickets = 100;
    @Override
    public void run() {
        while (true) {
            if (tickets <= 0) {
                System.out.println("票已售罄...");
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("成功买票"+Thread.currentThread().getName()+
                    "还剩"+(--tickets)+"张票");
        }
    }
}

运行后会发现,这个程序会有些问题,会发生超卖问题,这个问题设计后面的知识点,在后面的时候再解决

  • 线程终止:

    添加一个条件让线程停止运行

public class A{
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
        Thread.sleep(1000);
        t.setLoop(false);
    }
}
class T extends Thread{
    private int count = 0;
    private boolean loop = true;
    
    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        while (loop){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("运行中..." + (++count));
        }
    }
}
  • 线程常用方法:

    yield:线程的礼让,让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功

    join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入线程所有的任务

public class A{
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
       for (int i = 1; i < 20;i++){
           Thread.sleep(1000);
           System.out.println("主线程给了你"+i+"巴掌");
           if(i == 5){
               System.out.println("主线程先歇会,让子线程先上");
               t.join();
               System.out.println("子线程打完,主线程上");
           }
       }
    }
}
class T extends Thread{
    @Override
    public void run() {
        for (int i = 0;i <= 20;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程给了你" + i +"巴掌");
        }
    }
}

互斥锁

  • 基本介绍:

(1)java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性

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

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

(4)同步的局限性:导致程序的执行效率降低

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

(6)同步方法(静态的)的锁为当前类本身

  • 注意事项和细节

(1)同步方法如果没有使用static修饰:默认锁对象为this

(2)如果方法使用static修饰,默认所对象:当前类.class

释放锁

(1)当前线程的同步方法,同步代码块执行结束

(2)当前线程在同步代码块、同步方法中遇到break、return

(3)当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束

(4)当前线程在同步代码块、同步方法中执行了线程对象的wait方法,当前线程暂停,并释放锁

  • 以下情况不会释放锁

(1)线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁。

(2)线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,不会释放锁

  • 练习题:

(1)在main方法中启动两个线程

(2)第一个线程循环随机打印100以内的整数

(3)直到第二个线程从键盘读取了Q命令

public class A{
    public static void main(String[] args){
        One one = new One();
        Two two = new Two(one);
        one.start();
        two.start();
    }
}
class One extends Thread{
    private boolean loop = true;

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

    @Override
    public void run() {
        while (loop ){
//                随机输出1 - 100之间的数
                System.out.println((int) (Math.random() * 100 + 1));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }
}
class Two extends Thread{
    private One one;
    private Scanner myScanner = new Scanner(System.in);

    public Two(One one) {
        this.one = one;
    }

    @Override
    public void run() {
        System.out.println("请输入Q表示退出:");
        char q = myScanner.next().toUpperCase().charAt(0);
        while (true) {
            if (q == 'Q') {
//            以通知的方式使A停止
                one.setLoop(false);
                System.out.println("b线程退出");
                break;
            }
        }
    }
}

(2)两个人从银行里取钱,总金额10000,每次取一千,当余额不足时,提示余额不足

public class A{
    public static void main(String[] args){
        One one = new One();
        Thread thread = new Thread(one);
        Thread thread1 = new Thread(one);
        thread.start();
        thread1.start();
    }
}
class One implements Runnable{
    private double money = 10000.0;
    private boolean loop = true;

    @Override
    public void run() {
                while (loop) {
                    synchronized (this) {
                        if (money < 1000) {
                            System.out.println("余额不足,无法取出");
                            loop = false;
                            break;
                        }
                        money -= 1000;
                        System.out.println("您已成功取钱,还剩" + money);
                    }

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值