Java技术复习提升 14多线程基础

第14章 多线程基础

14.1 线程

几个基本概念:

程序:我们写的代码

进程:运行中的程序 或者说程序的一次执行过程 是动态的 有本身产生 存在 消亡的过程

线程:由进程创建 作为进程的一个实体 一个进程可以有多个线程

单线程:同一时刻只能执行一个线程

多线程:同一时刻可以执行多个线程

并发:同一时刻 多个任务交替执行 看起来是貌似同时 举例:单核CPU实现的多任务

并行:同一时刻 多个任务同时执行 举例:多核CPU可以实现并行

14.2 线程基本使用

创建线程两种方式

  • 继承Thread类 重写run方法

import javax.xml.transform.Source;
import java.sql.SQLOutput;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Cat cat=new Cat();
        cat.start();

        System.out.println("主线程继续执行");
        for(int i=1;i<=10;i++){
            System.out.println("小狗叫 "+i+" "+Thread.currentThread().getName());
            Thread.sleep(1000);
        }
    }
}

class Cat extends Thread{
    int times=0;
    @Override
    public void run(){
        while(true){
            System.out.println("小猫叫 "+(++times)+" "+Thread.currentThread().getName());
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            if(times==20){
                break;
            }
        }
    }
}

所以在控制台的输出为先main的Thread执行 再Cat的Thread执行 说明在调用cat.run()之后 只是发出请求告知可以执行 具体取决于CPU 

  • 实现Runnable接口 重写run方法

首先 因为java是单继承的 如果一个类已经继承了某个父类 再继承Thread类显然不现实 所以有另一种方式 实现Runnable接口来创建线程

import javax.xml.transform.Source;
import java.sql.SQLOutput;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Cat cat=new Cat();
        Thread thread=new Thread(cat);
        thread.start();

        System.out.println("主线程继续执行");
        for(int i=1;i<=10;i++){
            System.out.println("小狗叫 "+i+" "+Thread.currentThread().getName());
            Thread.sleep(1000);
        }
    }
}

class Cat implements Runnable{
    int times=0;
    @Override
    public void run(){
        while(true){
            System.out.println("小猫叫 "+(++times)+" "+Thread.currentThread().getName());
            try{
                Thread.sleep(1000);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            if(times==20){
                break;
            }
        }
    }
}

个人理解:

实际上创建线程都要借助thread类 只不过对于没有父类的类来说 可以直接继承thread并重写run方法进而调用它的start方法创建线程 而对于有父类的类来说 因为java的单线程特性 不能再继承thread 所以需要实现Runnable接口 再将该类的实例对象传入Thread 进而再调用thread的start方法

 14.3二者区别:

就上面的个人理解 本质上二者都是为了调用thread的start方法 没有区别 

但 实现Runnable接口方式更加适合多个线程共享一个资源的情况 而且避免的单继承的限制 所以推荐使用Runnable接口;而且 Runnable接口代码复用性比较高;而且实现Runnable接口的类只是定义了任务逻辑,它与Thread类是一种组合关系(通过Thread构造函数将Runnable实例传入)。这样,Thread类负责线程相关的操作,Runnable类负责任务逻辑,职责更加分明。

public class Test {
    public static void main(String[] args)  {
        sellticket1 window1=new sellticket1();
        sellticket1 window2=new sellticket1();
        sellticket1 window3 =new sellticket1();
        window1.start();
        window2.start();
        window3.start();


    }
}

class sellticket1 extends Thread{
    private int ticket_count=100;
    @Override
    public void run(){
        while (true){
            if(ticket_count<0){
                System.out.println("票售罄");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票 剩余票数:"+(--ticket_count));

        }
    }
}

class selltickets2 implements Runnable{
    private int ticket_count=100;
    @Override
    public void run(){
        while (true){
            if(ticket_count<0){
                System.out.println("票售罄");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票 剩余票数:"+(--ticket_count));

        }
    }
}

很明显 出现了超卖的问题 

public class Test {
    public static void main(String[] args)  {
        sellticket2 window1=new sellticket2();
        sellticket2 window2=new sellticket2();
        sellticket2 window3 =new sellticket2();

        new Thread(window1).start();
        new Thread(window2).start();
        new Thread(window3).start();
    }
}

class sellticket1 extends Thread{
    private int ticket_count=100;
    @Override
    public void run(){
        while (true){
            if(ticket_count<0){
                System.out.println("票售罄");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票 剩余票数:"+(--ticket_count));

        }
    }
}

class sellticket2 implements Runnable{
    private int ticket_count=100;
    @Override
    public void run(){
        while (true){
            if(ticket_count<0){
                System.out.println("票售罄");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票 剩余票数:"+(--ticket_count));

        }
    }
}

 

同样出现了超卖问题

14.4线程终止

  • 线程完成任务后 会自动退出
  • 还可以通过使用变量控制run方法退出 即通知方式
public class Test {
    public static void main(String[] args)  {
        sellticket1 window1=new sellticket1();
        sellticket1 window2=new sellticket1();
        sellticket1 window3 =new sellticket1();


        window1.start();
        window2.start();
        window3.start();

        window1.setLoop(false);


    }
}

class sellticket1 extends Thread{
    private int ticket_count=100;
    private boolean loop=true;

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

    @Override
    public void run(){
        while (loop){
            if(ticket_count<0){
                System.out.println("票售罄");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票 剩余票数:"+(--ticket_count));

        }
    }
}

class sellticket2 implements Runnable{
    private int ticket_count=100;
    @Override
    public void run(){
        while (true){
            if(ticket_count<0){
                System.out.println("票售罄");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票 剩余票数:"+(--ticket_count));

        }
    }
}

可以看到 已经没有window0的售票记录 因为我们将window0的loop设置为false

14.5 线程常用方法

 

class Test{
    public static void main(String[] args) throws InterruptedException {
        T t=new T();
        t.setName("byx");
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();

        for(int i=0;i<5;i++){
            Thread.sleep(1000);
            System.out.println(i);
        }
        System.out.println(t.getPriority());
        t.interrupt();//将主线程打断
    }
}

class T extends Thread{
    @Override
    public void run(){
        while (true){
            for(int i=0;i<100;i++){
                System.out.println(Thread.currentThread().getName()+i);
            }
            try {
                System.out.println(Thread.currentThread().getName()+"休眠中");
                Thread.sleep(20000);
            }catch (InterruptedException e){
                System.out.println(Thread.currentThread().getName()+"被interrupt了");
            }
        }
    }
}

 

public class ThreadMethod02 {
    public static void main(String[] args) throws InterruptedException {
        T2 t2 = new T2();
        t2.start();
        for(int i = 1; i <= 20; i++) {
            Thread.sleep(1000);
            System.out.println("主线程(小弟) 吃了 " + i  + " 包子");
            if(i == 5) {
                System.out.println("主线程(小弟) 让 子线程(老大) 先吃");
                //join, 线程插队
                t2.join();// 这里相当于让t2 线程先执行完毕
                //Thread.yield();//礼让,不一定成功..
                System.out.println("线程(老大) 吃完了 主线程(小弟) 接着吃..");
            }
        }
    }
}
class T2 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            try {
                Thread.sleep(1000);//休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程(老大) 吃了 " + i +  " 包子");
        }
    }
}

 join()线程插队一定成功

 

yield()线程礼让不一定成功 因为礼让时间不一定

练习:

 

public class ThreadMethod02 {
    public static void main(String[] args) throws InterruptedException {
        Thread t2 = new Thread(new T2());
        for(int i = 1; i <= 10; i++) {
            Thread.sleep(1000);
            System.out.println("hi"+i);
            if(i == 5) {
                t2.start();
                t2.join();
            }
            Thread.sleep(1000);
        }
    }
}
class T2 extends Thread {
    int count=0;
    @Override
    public void run() {

        while (true){
                System.out.println("hello"+(++count));
                try {
                    Thread.sleep(1000);//休眠1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(count==10){
                    break;
                }
        }


    }
}

用户线程和守护线程

public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        //如果我们希望当main线程结束后,子线程自动结束
        //,只需将子线程设为守护线程即可
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();
        for( int i = 1; i <= 10; i++) {//main线程
            System.out.println("宝强在辛苦的工作...");
            Thread.sleep(1000);
        }
    }
}
class MyDaemonThread extends Thread {
    public void run() {
        for (; ; ) {//无限循环
            try {
                Thread.sleep(1000);//休眠1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~");
        }
    }
}

 

 14.6 线程生命周期

 

14.7 Synchronized

 线程同步机制

同步具体方法

 

 原理:

 

14.8 互斥锁

 

 

package com.hspedu.syn;
public class SellTicket {
    public static void main(String[] args) {
        //测试一把
        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方法变量
    Object object = new Object();

    //同步方法(静态的)的锁为当前类本身
    //解读
    //1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
    //2. 如果在静态方法中,实现一个同步代码块.
    /*
        synchronized (SellTicket03.class) {
            System.out.println("m2");
        }
     */
    public synchronized static void m1() {}
    public static  void m2() {
        synchronized (SellTicket03.class) {
            System.out.println("m2");
        }
    }

    //说明
    //1. public synchronized void sell() {} 就是一个同步方法
    //2. 这时锁在 this对象
    //3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在this对象
    public /*synchronized*/ void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法
        synchronized (/*this*/ object) {
            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));//1 - 0 - -1  - -2
        }
    }
    @Override
    public void run() {
        while (loop) {
            sell();//sell方法是一共同步方法
        }
    }
}

 

 总的来说,this对象用于实例方法的同步,确保同一对象实例的同步方法在同一时刻只有一个线程访问;类对象用于静态方法的同步,确保对于整个类的静态同步方法在同一时刻只有一个线程访问。这是 Java 为了适应实例级别和类级别不同的同步需求而设计的机制。

 14.9 线程死锁

14.10 释放锁场景:

 不会释放锁场景:

14.11 银行取钱


public class Homework02 {
    public static void main(String[] args) {
        T t = new T();
        Thread thread1 = new Thread(t);
        thread1.setName("t1");
        Thread thread2 = new Thread(t);
        thread2.setName("t2");
        thread1.start();
        thread2.start();
    }
}
//编程取款的线程
//1.因为这里涉及到多个线程共享资源,所以我们使用实现Runnable方式
//2. 每次取出 1000
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 ,准备继续争夺
            //5. this对象锁是非公平锁.
            synchronized (this) {//
                //判断余额是否够
                if (money < 1000) {
                    System.out.println("余额不足");
                    break;
                }
                money -= 1000;
                System.out.println(Thread.currentThread().getName() + " 取出了1000 当前余额=" + money);
            }
            //休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
 

 

2024/11/26 1:29了 作者困得不行了 明早还有课 所以最后synchronized囫囵吞枣 请各位读者监督明天再顺一遍 并复习完IO流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值