Java基础__学习笔记__线程

 

-- 进程是执行中的一段程序,而一个进程中执行中的每个任务即为一个线程

-- 一个线程只可以属于一个进程,但一个进程能包含多个线程

-- 线程无地址空间,它包括在进程的地址空间里

线程和线程可以有依赖关系也可以没有,一个线程挂了不会影响另一个线程。主线程开启子线程,主线程挂了,子线程不一定会挂。

Thread实现了Runnable接口

 

 

 

一个类继承了Thread可以用这个类的实例化对象开启一个线程。这个类必须重写run方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.leehl.thread;
 
public class MyThread3 {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());//main线程
        Menglei a = new Menglei();
        a.start();
 
    }
}
class Menglei extends Thread{
    int time;
    @Override
    public void run() {
        while (true) {
            try {
                Menglei.sleep(1000);    //可以使用Thread的Thread.currentThread().getName()方法获得线程名
                System.out.println("梦泪老师说扣1送地狱火"+(++time)+"线程名:"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(time==8){
                break;
            }
        }
 
    }
}

 

 

 

 

 如果是a.run()那么程序会串行化执行,进程也都是main线程。

当执行start方法后,会在start中执行执行start0方法,start0方法底层是由JVM底层调用native方法,C/C++实现的

所以真正实现多线程效果的是start0()方法。

而start0()方法只是把这个线程变成了可运行状态,最终还是要看CPU的的调度算法,什么时候调用

public synchronized void start() {

start0();
}

start执行start方法后:

 

 

当一个类已经继承了一个父类后,没法继承Thread,我们可以实现Runnable接口来实现线性

                   

 

复制代码
package com.leehl.Threadproxy;

public class MyThread4 {
    public static void main(String[] args) {
        dog d = new dog();
        d.setCount(15);
        Thread t = new Thread(d);
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("陈明旭和罗昊在狗叫卢毅"+Thread.currentThread().getName());
        }



//__________________________________________________________________________________________

        //想让tiger走其他线程,但是tiger已经继承了animal类,无法通过继承Thread类来调用start()方法
        //但是tiger又要走别的线程,所以就找了个代理类ProxyThread来帮tiger类找一下start()方法
        tiger tiger = new tiger();
        ThreadProxy threadProxy = new ThreadProxy(tiger);
        threadProxy.start();
    }
}

class animal{}
class tiger extends animal implements Runnable{
    @Override
    public void run() {
        System.out.println("----老虎----"+Thread.currentThread().getName());
    }
}

class ThreadProxy implements Runnable{
    private Runnable target=null; //定义一个runnable来接
    @Override
    public void run() {
        if(target != null){
            target.run();
        }
    }

    //调用start方法 实际上还是main线程
    public ThreadProxy(Runnable target) { //线程代理,只要实现了Runnable就可以使用这个构造器
        this.target = target;
    }
    public void start(){
        start0();
    }

    private void start0() {
        run();
    }
}
//__________________________________________________________________________________________


class dog implements Runnable{
    private int count;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println("卢毅扣1送地狱火烧陈明旭和罗昊"+(++count)+Thread.currentThread().getName());

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(count==20){
                break;
            }
        }
    }
}
复制代码

 

常见线程方法:

setName()  设置线程名称

getName()  返回线程名称

start 使线程开始执行,实际调用start0方法

run 调用线程对象run方法

setPriority 更改优先级

getPriority 获取线程的优先级

sleep() 让线程指定毫秒内休眠

interrupt 中断线程

isinterrupt 返回该线程是否被中断 Boolean类型

yield 线程礼让 但不一定会成功

join 线程插队,一旦插队成功,肯定会先完成插入的线程内的所有任务

线程名.setDaemon(true) 守护线程,当main线程结束后,守护线程也就自动接受了

 

线程中断方法 :调用Thread里面的interrupt方法来打断线程的运行(可以通过isinterrupt来判断这个线程是否被打断)

还是之前强调过的:Java里的线程最终的执行和调度都是由操作系统来决定的,JVM只是对操作系统层面的线程做了一层包装而已。

所以我们在Java里面通过start方法启动一个线程的时候,只是告诉操作系统这个线程可以被执行,但是最终交给CPU来执行是操作系统的调度算法来决定的。

复制代码
package com.leehl.thread;

public class MyThread5 {
    public static void main(String[] args) throws InterruptedException {
        T a1 = new T();
        a1.start();
        Thread.sleep(1000);
        System.out.println("111111111111地狱火");
        a1.interrupt();
    }
}
class T extends Thread{
    @Override
    public void run() {
        while (true){
            if (this.isInterrupted()) {
                System.out.println(Thread.currentThread().getName()+"线程被interrupt了");
                break;
            }
            System.out.println("----");
        }
    }
}
复制代码

 

 

 

售票问题:

复制代码
package com.leehl.Sellticket;

import java.io.BufferedReader;

/**
 * 模拟三个窗口同时售票100张
 */
public class ticket {
    public static void main(String[] args) {


//        sellticket01 sellticket01 = new sellticket01();
//        sellticket01 sellticket02 = new sellticket01();
//        sellticket01 sellticket03 = new sellticket01();
//        sellticket01.start();
//        sellticket02.start();
//        sellticket03.start();
//        //会出现超卖的现象:当ticketNum=1的时候,线程1通过判断if语句满足条件进入后,还没执行(--ticketNum),
//        //此时线程2也通过判断if语句也进入了,也没执行(--ticketNum),
//        //但此时票数只有一张,两个线程都会进行(--ticketNum),导致超卖。
        
        
        sellticket02 sellticket02 = new sellticket02();
        Thread a1 = new Thread(sellticket02);
        Thread a2 = new Thread(sellticket02);
        Thread a3 = new Thread(sellticket02);
        a1.start();
        a2.start();
        a3.start();
        //还是会出现超卖,和上述问题一样


    }
}

//使用Thread方式
class sellticket01 extends Thread{
    private static int ticketNum = 100; //让多个线程共享这个ticketNum

    @Override
    public void run() {
        while (true) {
            if(ticketNum <= 0){
                System.out.println("票卖完了");
                break;
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("窗口:"+Thread.currentThread().getName()+"卖出了一张票,剩余票数"+(--ticketNum));

        }
    }
}

//通过继承Runnable的方式
class sellticket02 implements Runnable{
    private int ticketNum = 100; //因为只有一个对象,多个线程共享这个对象,所以ticketNum可以不用static

    @Override
    public void run() {
        while (true) {
            if(ticketNum <= 0){
                System.out.println("票卖完了");
                break;
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("窗口:"+Thread.currentThread().getName()+"卖出了一张票,剩余票数"+(--ticketNum));

        }
    }
}
复制代码

 

 

上面两种方法都会出现票超卖的现象,问题在于我们去理解调用线程的这个过程

 

解决方法:

单独把sell方法拎出来,设置为synchronized,这样每个线程在执行sell方法的时候,会有一个互斥锁。拥有该锁的"钥匙"才能去执行sell方法,否则会被卡在等待区被执行


复制代码
class sellticket03 implements Runnable{
    private int ticketNum = 1000; //因为只有一个对象,多个线程共享这个对象,所以ticketNum可以不用static
    boolean loop = true;
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }

    //public synchronized void sell{}就是一个同步方法
    //此时锁在this对象
    //也可以在代码块上写synchronized
    public synchronized void sell() { //同步方法,在同一时刻,只能有一个线程来执行run方法

        if(ticketNum <= 0){
            System.out.println("票卖完了");
            loop = false;

            return;
        }
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口:"+Thread.currentThread().getName()+"卖出了一张票,剩余票数"+(--ticketNum));
    }
}
复制代码

 

 

也可以把 sell方法里面的对象this用synchronize锁住

复制代码
//通过实现Runnable接口,使用Synchronized实现线程同步

class sellticket03 implements Runnable{
    private int ticketNum = 1000; //因为只有一个对象,多个线程共享这个对象,所以ticketNum可以不用static
    boolean loop = true;
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }

    //public synchronized void sell{}就是一个同步方法
    //此时锁在this对象
    //也可以在代码块上写synchronized
    public  void sell() { //同步方法,在同一时刻,只能有一个线程来执行run方法
        synchronized(this){
            if (ticketNum <= 0) {
                System.out.println("票卖完了");
                loop = false;
                return;
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口:" + Thread.currentThread().getName() + "卖出了一张票,剩余票数" + (--ticketNum));
        }
    }
}
复制代码

 

或者:

复制代码
//通过实现Runnable接口,使用Synchronized实现线程同步

class sellticket03 implements Runnable{
    private int ticketNum = 1000; //因为只有一个对象,多个线程共享这个对象,所以ticketNum可以不用static
    boolean loop = true;
    Object o = new Object();
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }

    //public synchronized void sell{}就是一个同步方法
    //此时锁在this对象
    //也可以在代码块上写synchronized
    public  void sell() { //同步方法,在同一时刻,只能有一个线程来执行run方法
        synchronized(o){
            if (ticketNum <= 0) {
                System.out.println("票卖完了");
                loop = false;
                return;
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口:" + Thread.currentThread().getName() + "卖出了一张票,剩余票数" + (--ticketNum));
        }
    }
}
复制代码
sellticket03 sellticket03 = new sellticket03();
Thread a1 = new Thread(sellticket03);
Thread a2 = new Thread(sellticket03);
Thread a3 = new Thread(sellticket03);
a1.start();
a2.start();
a3.start();
三个线程其实都是操作同一个线程 object o ,所以用synchronized也可以锁住,这也是要求我们多个线程的锁对象是同一个即可,如果我们把上面的代码
synchronized(o) 变为 synchronized(new object),那么在红色代码这块,每个线程会创建一个新的object对象,不会上锁,锁的对象操作的不是一个。

如果这个sell方法是一个静态的static,那么这个锁就不在this上了,而是加在这个类class上的。
如果在静态方法中要实现一个同步代码块,要用这个class类本身上,不能用this,静态方法中本身就不能用this。

 

 

 

 

 

posted @   Lee最好好好吃饭  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示