java-线程

程序

是为了完成特定任务,用某种语言编写的一组指令的集合

进程

进程是指运行中的程序。进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程,有他自身的产生,存在和消亡过程。

线程

线程是由进程创建的,是进程的一个实体。一个进程可以拥有多种线程

  • 单线程: 同一个时刻,只允许执行一个线程
  • 多线程: 同一个时刻,可以执行多个线程。
  • 并发: 同一个时刻,多个任务交替执行。(单核cpu实现的多任务就是并发)
  • 并行: 同一个时刻,多个任务同时执行,多核cpu可以实现并行。

创建线程

image.png

继承Thread类 创建线程

package com.xxb.threaduse;

public class Thread01 {
    public static void main(String[] args) {
        //创建Cat对象,可以当做线程
        Cat cat = new Cat();
        //启动线程
        cat.start();
        //说明:当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行。
        System.out.println("主线程继续执行"+Thread.currentThread().getName());
        for (int i = 0; i <60 ; i++) {
            System.out.println("主线程 i="+i);
            //让主线程休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//1.当一个类继承了Thread类 该类就可以当线程使用
//2. 我们会重写run方法,写上自己的业务代码
//3. run Thread类实现的Runnable接口的run方法
class Cat extends Thread {
    int times=0;
    //重写run方法 写上自己的业务逻辑
    @Override
    public void run() {
        while (true) {
            //该线程每隔1s,在控制台输出"喵喵,我是小猫咪"
            System.out.println("喵喵,我是小猫咪"+(++times)+"线程名="+Thread.currentThread().getName());
            //让该线程休眠1s  ctrl+alt+t
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(times==80){
                break;
            }
        }
    }
}

image.png

image.png

Runnable创建线程(使用Thread.proxy)

image.png

package com.xxb.threaduse;

public class Thread02 {
    public static void main(String[] args) {
        Dog dog=new Dog();
        Thread thread = new Thread(dog);
        thread.start();
    }
}
class Dog implements  Runnable{
    int count=0;
    @Override
    public  void run(){
        while(true){
            System.out.println("小狗汪汪叫"+(++count)+Thread.currentThread().getName());
            //休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count==10){
                break;
            }
        }
    }
}

image.png

多线程执行

package com.xxb.threaduse;

/**
 * main线程启动2个子线程
 */
public class Thread03 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        //开启两个子线程
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();
        thread2.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==20){
                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==10){
                break;
            }
        }
    }
}

image.png

继承Thread 与实现Runnable的区别

image.png

案例:多线程售票(会出现超卖问题)

package com.xxb.ticket;

/**
 * 使用多线程,模拟三个窗口同时售票
 */
public class Sellticket {
    public static void main(String[] args) {
        //测试
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
        //这里会出现超卖的问题。
//        sellTicket01.run();
//        sellTicket02.run();
//        sellTicket03.run();

        //Runable 方式
        SellTicket02 sellTicket02 = new SellTicket02();
        //这里也会出现超卖的问题
        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();
    }
}
//使用Thread方式
class SellTicket01 extends Thread{
    //让多个线程共享 ticketNum
    private static int ticketNum=2;
    @Override
    public void run(){
        while (true){
            if(ticketNum<=0){
                System.out.println("售票结束");
                break;
            }
            //休眠50ms
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));

        }
    }
}
class SellTicket02 implements Runnable{
    //让多个线程共享 ticketNum
    private  int ticketNum=100;
    @Override
    public void run(){
        while (true){
            if(ticketNum<=0){
                System.out.println("售票结束");
                break;
            }
            //休眠50ms
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));

        }
    }
}

image.png

线程终止(通知线程退出)

  • 当线程完成任务后,会自动退出
  • 还可以通过使用变量来控制run方法退出的方式停止线程。
package com.xxb.exit;

public class ThreadExit_ {
    public static void main(String[] args) {
        T t1 = new T();
        t1.start();
        //如果希望main线程去控制t1线程的终止,必须可以修改loop
        //让t1退出run方法,从而终止t1线程
        //让主线程休眠10s,再通知t1线程退出
        try {
            Thread.sleep(10*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.setLoop(false);
    }
}
class T extends Thread{
   private int count=0;
   //设置一个控制变量
    private boolean loop=true;
    @Override
    public void run(){
        while(loop){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("AThread 运行中..."+ (++count));

        }
    }

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

image.png

线程常用方法

image.png

image.png

线程中断

package com.xxb.method;

public class ThreadMethod01 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setName("cnm");
        //最低优先级
        t.setPriority(Thread.MIN_PRIORITY);
        System.out.println(t.getName());
        //启动线程
        t.start();
        //主线程打印5 句hi  然后我就中断 子线程的休眠
        for(int i=0;i<5;i++){
            Thread.sleep(1000);
            System.out.println("hi "+i);
        }
        System.out.println(t.getName()+" 线程的优先级=" +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);

            }
            System.out.println(Thread.currentThread().getName() + " 休眠中。。。");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                //当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
                //InterruptedException 是捕获到一个中断异常
                System.out.println(Thread.currentThread().getName() + "被 interrupet了");

            }
        }
    }
}


image.png

线程插队

package com.xxb.method;

public class ThreadMethod02{
    public static void main(String[] args) throws InterruptedException {
        T2 t2 = new T2();
        t2.setName("cnm");

        //启动线程
        t2.start();

        for(int i=0;i<20;i++){
            Thread.sleep(1000);
            System.out.println("主线程(小弟)吃包子  "+i);
            if(i==5){
                System.out.println("主线程(小弟)让子线程(老大)先吃");
                //这里相当于t2线程先执行 让t2插队
                t2.join();
                //礼让 不一定成功
//                Thread.yield();
                System.out.println("子线程(老大)吃完了 主线程(小弟)接着吃");
            }
        }

    }
}

class T2 extends Thread{
    @Override
    public  void run() {
//        while (true) {
            for (int i = 0; i < 20; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();

                }
                System.out.println(Thread.currentThread().getName() + " 子线程吃包子。。。" + i);

            }


//        }
    }
}

image.png

用户线程和守护线程

image.png

package com.xxb.method;

public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {
        //守护线程
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        //将子线程设置守护线程即可
        myDaemonThread.setDaemon(true);
        //开启线程
        myDaemonThread.start();

        for(int i=1;i<=10;i++){
            System.out.println("fck");
            Thread.sleep(1000);
        }
    }
}
//守护线程
class MyDaemonThread extends Thread{
    @Override
    public  void run(){
        for(;;){//无限循环
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("不抱怨,少说话,多做事");
        }
    }
}

image.png

线程的生命周期

image.png

image.png

线程的同步机制(Synchronized)

  1. 在多线程编程,一些敏感数据不允许别多个线程同时访问,此时就使用同步访问技术,保证数据再任何同一时刻,最多有一个线程访问,以保证数据的完整性。
  2. 也可以这样理解: 线程同步,即当有一个线性在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

同步的具体方法

image.png

用同步方法解决卖票超卖问题。

package com.xxb.sync;

/**
 * 使用多线程,模拟三个窗口同时售票
 */
public class Sellticket {
    public static void main(String[] args) {
        //测试
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
        //这里会出现超卖的问题。
//        sellTicket01.run();
//        sellTicket02.run();
//        sellTicket03.run();

        //Runable 方式
//        SellTicket02 sellTicket02 = new SellTicket02();
//        //这里也会出现超卖的问题
//        new Thread(sellTicket02).start();
//        new Thread(sellTicket02).start();
//        new Thread(sellTicket02).start();
//        同步
        SellTicket03 sellTicket03 = new SellTicket03();
        //这里也会出现超卖的问题
        new Thread(sellTicket03).start();
        new Thread(sellTicket03).start();
        new Thread(sellTicket03).start();
    }
}

//使用Thread方式
class SellTicket01 extends Thread{
    //让多个线程共享 ticketNum
    private static int ticketNum=2;
    @Override
    public void run(){
        while (true){
            if(ticketNum<=0){
                System.out.println("售票结束");
                break;
            }
            //休眠50ms
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));

        }
    }
}

class SellTicket02 implements Runnable{
    //让多个线程共享 ticketNum
    private  int ticketNum=100;
    @Override
    public void run(){
        while (true){
            if(ticketNum<=0){
                System.out.println("售票结束");
                break;
            }
            //休眠50ms
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));

        }
    }
}
//实现接口方式, 使用synchronized实现线程同步
class SellTicket03 implements Runnable{
    //让多个线程共享 ticketNum
    private  int ticketNum=100;
    //控制变量
    private boolean loop=true;
    //同步方法,在同一时刻,只有一个线程来执行run方法
    public synchronized void sell(){
        if(ticketNum<=0){
            System.out.println("售票结束");
            loop=false;
            return;
        }
        //休眠50ms
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));

    }

    @Override
    public  void run(){
        while (loop){
            this.sell();
        }
    }
}

image.png

互斥锁

image.png

线程执行图

image.png

锁在方法上

//锁在方法上
public synchronized void sell(){
        if(ticketNum<=0){
            System.out.println("售票结束");
            loop=false;
            return;
        }
        //休眠50ms
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));

    }

同步代码块

 public /*synchronized*/ void sell() {
        //同步代码块
        synchronized (this) {
            if (ticketNum <= 0) {
                System.out.println("售票结束");
                loop = false;
                return;
            }
            //休眠50ms
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
        }


    }
//静态方法的锁为当前类本身  即锁是加在SellTicket03.class上。
    public  synchronized  static void m1(){

    }

//在静态方法中,实现一个同步代码块
  public  static  void m2(){
        synchronized (SellTicket03.class){
            System.out.println("m2");
        }
    }

互斥锁注意事项

image.png

线程的死锁

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

释放锁

image.png

image.png

posted @ 2021-09-28 21:48  sprite5521  阅读(53)  评论(0)    收藏  举报