java线程

线程

在操作系统中我们引入进程是为了使多个程序能够并发执行,以提高资源利用率,那么引入线程就是为了减少程序在并发执行所付出的时间开销

进程的基本属性

  1. 我们知道进程是一个可拥有独立资源的单位,一个进程要能独立运行,他必须要有一定的资源。
  2. 进程同时又是一个可以独立调度和分配的基本单位。

并发和并行

并发性和并行性是两个相似但有区别的两个概念

  • 并发性

    两个或多个事件在同一时间间隔内发生

  • 并行性

    两个或多个事件在同一时刻发生

那么程序并发执行需要付出的时空开销需要完成以下的操作:

  1. 创建进程。分配其必须的、除处理机外的所有资源
  2. 撤销进程。先对其占有的资源执行回收操作,然后再撤销PCB(进程控制块)
  3. 进程切换。对进程进行上下文切换,需要保留当前进程的CPU环境,设置新的花费处理机时间。

由于进程是一个资源的拥有者,因而在创建、撤销、和切换中,系统必须为之付出极大的系统开销,所以我们就引入了线程。

线程的基本属性

线程作为调度和分配的基本单位(可以独立执行程序)

这样可以做到拥有资源的基本单位不施以频繁的切换,从而节省开销

单线程和多线程

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

线程的基本使用

  • 创建线程的两种方式
    1. 继承Thread类,重写run方法
    2. 实现Runnable接口,重写run方法

在这里插入图片描述

  1. 继承Thread类,重写run方法
public class Thread01 {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.start();//启动线程,最终会执行cat的run方法
    }
}
class Cat extends Thread{
    @Override
    public void run() {
        super.run();
        int count=0;
        while (true) {
            System.out.println("我是一只猫,快乐的星猫。"+(++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count==8){
                break;
            }
        }
    }
}

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

  • java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能
  • java提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程
public class Thread02 {
    public static void main(String[] args) {//代理模式
        Dog dog = new Dog();
        Thread thread = new Thread(dog);//不能直接使用dog.start()会报错,因为没有start方法,但thread有
        thread.start();
    }
}
class Dog implements Runnable{

    int count=0;
    @Override
    public void run() {
        while (true){
            System.out.println("小狗汪汪叫hi..."+(++count)+Thread.currentThread().getName());

            //休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count==10){
                break;
            }
        }
    }
}
  • 继承Thread vs 实现Runnable的区别

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

多线程机制

​ 进程在执行时先执行main()线程,当其碰到start方法时,执行另一个新的线程,这些新的线程也可以创建子线程,当所有的线程结束时,程序才会终止。

既然他最后是调用的run方法,为什么不直接调用run方法呢?

由于run方法只是一个简单地方法,并不会启动一个线程

我们来看一下源码:

public synchronized void start() {
            start0();
}

start0()是本地方法,是JVM调用,底层是c/c++实现

真正实现多线程的效果是start0(),而不是run

private native void start0();

线程终止

  1. 当线程完成任务后,会自动退出
  2. 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
public class ThreadExit01 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();

        //如果希望main线程去控制t1线程的终止,必须可以修改 loop
        //让 t1 退出run方法,从而终止 t1线程 -

        Thread.sleep(10*1000);
        t.setLoop(false);
    }
}
class T extends Thread{

    //设置一个控制变量
    private boolean loop = true;

    public boolean isLoop() {
        return loop;
    }

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

    @Override
    public void run() {
        while (loop){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("T运行中。。。");
        }
    }
}

线程中断

public class Method01 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setName("线程一");//设置线程名称
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();



        for (int i = 0;i<5;i++){
            Thread.sleep(1000);
            System.out.println("hi"+i);
        }

        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了");
            }
        }
    }
}

线程插队

  1. yield:线程的礼让,让出cpu,让其他的线程执行,但是礼让的时间不确定,所以也不一定成功
  2. join:线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程的所有的任务。
public class Method02 {
    public static void main(String[] args) throws InterruptedException {
        T2 t2 = new T2();
        t2.start();

        for (int i =1 ;i<=20;i++){
            System.out.println("主线程吃了"+i+"个包子");
            Thread.sleep(1000);
            if (i==5) {
                //join()
                //t2.join();
                //yield
                Thread.yield();
            }
        }
    }
}
class T2 extends Thread{
    @Override
    public void run() {
        for (int i=1;i<=20;i++){
            System.out.println("子线程(老大) 吃了"+i+"个包子");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  3. 常见的守护线程:垃圾回收机制
public class Method04 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        myDaemonThread.setDaemon(true);//设置守护线程
        myDaemonThread.start();
        for (int i =1;i<=5;i++){
            System.out.println("宝强在辛苦的工作");
            Thread.sleep(1000);
        }
    }//当主线程结束时,子线程也结束
}
class MyDaemonThread extends Thread{
    @Override
    public void run() {
        for (;;){//无限循环
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("马蓉和宋哲快乐的聊天");
        }
    }
}

线程的7种状态

在这里插入图片描述

线程同步机制

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

同步的具体方法 - Synchronized

  1. Synchronized在方法声明中,表示整个方法为同步方法
//使用多线程模拟三个窗口同时售票100
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();

//        SellTicket02 sellTicket01 = new SellTicket02();
//        SellTicket02 sellTicket02 = new SellTicket02();
//        SellTicket02 sellTicket03 = new SellTicket02();
//
//        Thread thread01 = new Thread(sellTicket01);
//        Thread thread02 = new Thread(sellTicket02);
//        Thread thread03 = new Thread(sellTicket03);
//
//        thread01.start();
//        thread02.start();
//        thread03.start();

        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();
        new Thread(sellTicket03).start();
        new Thread(sellTicket03).start();
    }
}

//使用Synchronized实现线程同步
class SellTicket03 implements Runnable{
    public static int ticketNum = 100;
    private boolean loop = true;//控制run方法的变量
        public  synchronized void sell(){//同步方法,同一时刻只能有一个线程执行sell
            if (ticketNum <= 0) {
                System.out.println("售票结束");
                loop=false;
                return;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数"+(--ticketNum));
        }
    @Override
    public  void run() {
        while (loop) {
            sell();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
//使用Thread方式(超卖)
class SellTicket01 extends Thread{
    public static int ticketNum = 1000;//多个线程共享num
    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                System.out.println("售票结束");
                break;
            }
            //休眠50毫秒,模拟
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数"+(--ticketNum));
        }
    }
}

//使用Runnable(超卖)
class SellTicket02 implements Runnable{

    @Override
    public void run() {
            while (true) {
                if (SellTicket01.ticketNum <= 0) {
                    System.out.println("售票结束");
                    break;
                }
                //休眠50毫秒,模拟
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("窗口"+Thread.currentThread().getName()+"售出一张票"+"剩余票数"+(--SellTicket01.ticketNum));
            }
        }
    }

2.同步代码块

public  synchronized void sell() {//同步方法,同一时刻只能有一个线程执行sell
        if (ticketNum <= 0) {
            System.out.println("售票结束");
            loop = false;
            return;
        }
        //休眠50毫秒,模拟
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数" + (--ticketNum));
    }

互斥锁

  1. java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
  2. 每个对象都对应于一个可称为"互斥锁"的标记,这个标记用来保证在任一时刻,能有一个线程访问该对象
  3. 关键字synchronized来与对象互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
  4. 同步的局限性:导致程序的执行效率要降低
  5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象
//来自其他对象
Object object = new Object();
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) {
                    throw new RuntimeException(e);
                }
                System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数" + (--ticketNum));
            }
        }
  1. 同步方法(静态的)的锁为当前类本身
class SellTicket03 implements Runnable{
    public static int ticketNum = 100;
    private boolean loop = true;//控制run方法的变量
    Object object = new Object();


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

线程死锁

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

public class DeadLock_{
    public static void main(String[] args) {
        DeadLockDemo A = new DeadLockDemo(true);
        DeadLockDemo B = new DeadLockDemo(false);

        A.start();
        A.setName("A线程");
        B.start();
        B.setName("B线程");
    }
}
class DeadLockDemo extends Thread {
    static Object object1 = new Object();
    static Object object2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag) {
        this.flag = flag;
    }


    //当两个线程一个进入1一个进入3就发生死锁,因为会占用对方的资源
    @Override
    public void run() {
        if (flag) {
            synchronized (object1) {
                {
                    System.out.println(Thread.currentThread().getName() + "进入1");
                    synchronized (object2) {
                        System.out.println(Thread.currentThread().getName() + "进入2");

                    }
                }
            }
        } else {
            synchronized (object2) {
                System.out.println(Thread.currentThread().getName() + "进入3");
                synchronized (object1) {
                    {
                        System.out.println(Thread.currentThread().getName() + "进入4");
                    }
                }
            }
        }
    }
}

释放锁

  1. 当前线程的同步方法、同步代码执行结束。
  2. 当前线程在同步代码块遇到break 、return
  3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

不会释放锁

  1. 线程执行同步代码块或同步方法时,程序调用Threed.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
  2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁

).getName() + “进入3”);
synchronized (object1) {
{
System.out.println(Thread.currentThread().getName() + “进入4”);
}
}
}
}
}
}


==释放锁==

1. 当前线程的同步方法、同步代码执行结束。
2. 当前线程在同步代码块遇到break 、return
3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

**不会释放锁**

1. 线程执行同步代码块或同步方法时,程序调用Threed.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java中,线程是程序执行的基本单元,用于并发执行任务。每个线程都有自己的生命周期,包括创建、运行、阻塞和终止。线程的创建可以通过继承Thread类或实现Runnable接口来实现。 线程池则是Java中一种高效的线程管理机制,它预先创建一定数量的工作线程,并在需要执行任务时从线程池中获取线程进行处理,当任务完成后,线程会返回到线程池等待下一次调度,而不是立即结束。这样可以避免频繁地创建和销毁线程带来的开销,提高系统的性能和资源利用率。 以下是Java线程线程池的一些关键点: 1. **线程创建**: - **继承Thread类**:创建自定义线程类并重写run()方法。 - **实现Runnable接口**:创建Runnable接口的实现类,提供run()方法,然后用Thread构造函数创建Thread实例。 2. **线程状态**: - 新建(New):线程对象被创建但还未启动。 - 运行(Runnable):线程正在执行run()方法。 - 阻塞(Blocked):线程因某个条件而暂停,如I/O操作等待数据。 - 等待(Waiting):线程在调用wait()方法后,进入等待状态,直到被其他线程唤醒。 - 守护(Terminated):非守护线程完成或主线程结束,守护线程自动退出。 3. **线程池组件**: - ExecutorService:线程池的核心接口,提供了提交任务和控制线程的方法。 - ThreadPoolExecutor:实现了ExecutorService,包含核心线程数、最大线程数、任务队列等配置。 - ScheduledThreadPoolExecutor:支持定时和周期性任务。 4. **线程池的优势**: - **资源复用**:减少线程创建和销毁的开销。 - **线程管理和调度**:灵活设置线程数量、线程优先级和任务执行策略。 - **避免死锁**:由于任务有顺序地等待特定资源,减少了死锁的可能性。 - **可扩展性**:随着任务增加,线程池可以根据需要动态调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

网代慎平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值