韩顺平java之多线程基础


一、线程介绍

1.程序:为了完成特定任务,用某种语言写的一组指令的集合。(就是写的代码。)
2.进程:运行中的程序。比如开启一个qq,微信。每开启一个进程,操作系统就会为该进程分配内存空间。

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

3.线程:可以由进程创建,也可以由线程创建。是进程的一个实体。一个进程可以拥有多个线程。(如qq同时打开多个聊天框、迅雷同时下载多个东西。)
4.单线程和多线程
单线程:同一时刻只允许运行一个线程。
多线程:同一时刻可以运行多个线程。
5.并发和并行:
并发:同一时刻,多个任务交替执行。(单核cpu实现的多任务就是并发。)
并行:同一时刻,多个任务同时执行。(多核cpu可以实现并行。)

二、线程的基本使用

1.创建线程的两种方式

(1)继承Thread类,重写run方法。
(2)实现Runnable接口,重写run方法。
如:

public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        Cat cat = new Cat();
//        cat.run();
        cat.start();
        for (int i = 0; i < 30; i++) {
            System.out.println("主线程i="+i);
            Thread.sleep(1000);
        }
    }
}
//继承Thread类,重写run方法。
class Cat extends Thread{
    int times=0;
    @Override
    public void run() {
        while (true) {
            if (times == 50){
                break;
            }
            System.out.println("喵喵喵,我是一个小狗~"+(++times)+"线程名="+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

当子线程启动后,主线程并不会陷入阻塞,仍会继续执行。

即当子线程启动后,若是单核cpu,子线程和主线程就是交替执行的。若是多核cpu,两者就是同时执行的。
主线程和子线程还可以再开线程。

2.两种方式具体实现

public class Thread03 {
    public static void main(String[] args) {
        A a = new A();
        a.start();
        B b = new B();
        Thread thread = new Thread(b);
        thread.start();
    }
}
class A extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("hello world");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}
class B implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("hi");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

3.两种方式的区别

(1)两者创建线程本质上没有区别。
(2)实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制。

三、线程终止

基本说明:

(1)线程完成任务后,自然会自动退出。
(2)通知方式,即使用变量来控制run方法退出的方式停止线程。

四、线程常用方法

1.常用方法

在这里插入图片描述
在这里插入图片描述
sleep():是线程睡眠。
interrupt:中断正在休眠的线程。
在这里插入图片描述
yield():礼让,有可能不成功。
join():插入,强制执行插入线程。
线程T执行了yield方法,如果资源不紧张,cpu认为没有必要让出资源,就会不成功。

2.用户线程和守护线程

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

五、线程同步机制

(1)线程同步机制

1.对一些敏感数据,或区域,保证在任意同一时刻,最多有一个线程访问,保证数据的完整性。
2.也可以理解为:当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,知道该线程完成操作,其他线程才能对该内存地址进行操作。

(2)同步具体方法-synchronized

1.同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码。
//需要被同步的代码
}
2.同步方法
synchronized放在整个方法中,表示整个方法为同步方法。
public synchronized void m(String name){
//需要被同步的代码。
}

一般范围越小越好。

(3)售票问题

使用多线程,模拟3个窗口同时售票100张。

public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//        //用3个Thread同时开启了3个线程(3个类调用了3个方法。)
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

        System.out.println("====使用实现接口方式来售票====");
        SellTicket02 sellTicket02 = new SellTicket02();
        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();

    }
}
@SuppressWarnings({"all"})
class SellTicket01 extends Thread{
private static int ticketNum=100;

    @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));

        }
    }
}
@SuppressWarnings({"all"})
class SellTicket02 implements Runnable{
    private  int ticketNum=100;

    @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));

        }
    }
}

会出现超卖现象。

(4)使用synchronized解决售票问题。

@SuppressWarnings({"all"})
public class SellTicket {
    public static void main(String[] args) {
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//        //用3个Thread同时开启了3个线程(3个类调用了3个方法。)
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();

//        System.out.println("====使用实现接口方式来售票====");
//        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();
    }
}

@SuppressWarnings({"all"})
class SellTicket01 extends Thread {
    private static int ticketNum = 100;

    @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));

        }
    }
}

@SuppressWarnings({"all"})
class SellTicket02 implements Runnable {
    private int ticketNum = 100;

    @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));

        }
    }
}

@SuppressWarnings({"all"})
class SellTicket03 implements Runnable {
    private int ticketNum = 100;
    private boolean loop = true;
    Object obj = new Object();

    public /*synchronized*/ void sell() {
        synchronized (/*this*/obj) {
            if (ticketNum <= 0) {
                System.out.println("售票结束。。");
                loop = false;
                return;//break是结束while循环,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();
    }
}

哪个对象执行,谁就先持有这个锁。

synchronized在静态方法中的使用:

public synchronized static void m1(){

}
public static void m2(){
        synchronized (SellTicket.class){
            System.out.println("m2");
        }
}

六、线程的七大状态

在这里插入图片描述
在这里插入图片描述
Runnable又可细化为2个状态,Ready:就绪态和Running:运行态。
Runnable状态只是代表可以运行,但是并不代表正在运行。
当线程被挂起时,就由Running转变为Ready。是在内核发生的转变。
当使用Thread.yield(礼让)时就会Running转变成Ready(内核之中),这就是为什么yield可能不会成功。
当在主线程中使用t.join()后,对于主线程就处于一个waiting状态,等待线程t执行完。
Wait,join,park():waiting
Sleep,eait(),join(),park(),parkNanos()设计到时间就是TimedWaiting。
Blocked:线程等待锁就成了blocked状态,当获得锁后就重新称为Runnable状态。
TimeWaiting后涉及时间等待时间结束就可以了。
Waiting则需要notify(),notifyAll,unpark等方法重新进入到Runnable状态。
进入到Runnable(可运行状态)可能是Ready,也可能是Running。是否真的运行还要取决于内核的调度器。

七、线程互斥锁

1.基本介绍

在这里插入图片描述
1.每个对象都有一个被称为“互斥锁”的标记,这个标记保证在任意时刻,只能有一个线程来访问该对象。
2.关键字synchronized与对象互斥锁联系。当某对象用synchronized修饰时,表明该对象在任意时刻只能有一个线程来访问。
3.同步的局限性:导致程序的执行效率要降低。
4.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一对象)。

同一把锁对应同一个对象。

5.同步方法(静态的)的锁为当前类本身。类.class。

2.注意事项和细节

(1)非静态同步方法,默认锁对象为this。
(2)静态同步方法,默认锁对象为:当前类.class。
(3)实现步骤:
1.分析要上锁的代码。
2.选择使用同步代码块或同步方法。
3.要求多个线程的锁对象为同一个。

八、线程死锁

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值