JavaSE

Java多线程

一、什么是线程

1、进程的概念

进程是指可执行程序并存放在计算机存储器的一个指令序列,它是一个动态执行的过程、

2、线程的概念

线程是比进程还要小的运行单位,一个进程包含多个线程

二、创建线程

1、通过Thred类创建线程

class Mythread extend thread{
    public void(){
        System.out.println(getName()+"该线程正在执行");
    }
}
public class ThreadTest{
    public static void main(String[] args){
        Mythread mythred = new Mythread();
        mythred.start();
    }
}

2、实现Runnable接口创建线程

public class Mythred02{
    public static void main(String[] args){
        Dog dog = new Dog();
        Thread t = new Thread(dog);
        t.start;
    }
}
class dog implemnets Runnable{
    int count = 0;
    public void run(){
        while(true){
            System.out.println((count++)+Thread.currentThred().getName());
                    //休眠1秒
            try{
            Thread.sleep(1000);
            }catch(InterruptedException e){
            e.printStackTrace();
            }
            if(count){
                break;
            }
        }
    }
}

三、多线程实例

public class Mythred02{
    public static void main(String[] args){
        T1 t1 = new T1();
        T2 t1 = new T2();
        Thread t00 = new Thread(t1);
        Thread t01 = new Thread(t2);
        t00.start;
        t01.start;
    }
}
class T1 implemnets Runnable{
    int count = 0;
    public void run(){
        while(true){
            System.out.println((count++)+Thread.currentThred().getName());
                    //休眠1秒
            try{
            Thread.sleep(1000);
            }catch(InterruptedException e){
            e.printStackTrace();
            }
            if(count==10){
                break;
            }
        }
    }
}
class T2 implemnets Runnable{
    int count = 0;
    public void run(){
        while(true){
            System.out.println((count++)+Thread.currentThred().getName());
                    //休眠1秒
            try{
            Thread.sleep(1000);
            }catch(InterruptedException e){
            e.printStackTrace();
            }
            if(count==5){
                break;
            }
        }
    }
}

四、通知线程退出

需求:启动一个线程t,要求在main线程中去停止线程t

public class Mythred02 throws EXception{
    public static void main(String[] args){
        T t = new T();
        t.start;
        System.out.println("main线程休眠10秒");
        Thread,sleep(10*1000);
        t.setLoop(false);
    }
}
class T extends Thread{
    private int count = 0;
    private boolean loop = true;
    public void run(){
        while(loop){
        System.out.println((count++)+Thread.currentThred().getName());
        }
    }
    public void setLoop(boolean loop){
        this.loop = loop;
    }
}

五、线程中断

1、线程常用方法

第一组
1.setName //设置线程名称,使之与参数name相同
2.getName //返回该线程的名称
3.start //开始线程
4.run //调用线程对象run方法
5.setPriority //线程线程优先级
6.getPriority //获得线程优先级
7.sleep //正在执行的线程进入休眠(暂停执行)
8.interrupt //中断线程

第二组
1、yield:线程的礼让,让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。
2、join:线程插队,插队的线程一旦插队成功,则肯定先执行完插入的线程的所有任务

案例:
main线程创建一个子线程,每隔1秒输出hello,输出20次,主线程每隔1秒,输出hi,输出20次,要求:两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续。

public class Mythred02 throws EXception{
    public static void main(String[] args){
        T t = new T();
        t.start;
        for(int i=1;i<=20;i++){
        Thread.sleep(1000);
        System.out.println("主线程执行"+i+"次");
        System.out.println("子线程插队");
        t.join();
        }

}
class T extends Thread{
    public void run(){
        for(int i=1;i<=20;i++ ){
        try{
        Thread.sleep(1000);
        }catch(InterruptedException e){
        e.printStackTrace();
        }
        System.out.println("子线程执行"+i+"次");
        }
    }
}

用户线程和守护线程

  • 用户线程:也叫工作线程,当线程的任务执行完后或通知方式结束
  • 守护线程:一般是为了工作线程服务的,当所有的用户线程结束,守护线程自动结束
  • 常见的守护线程:垃圾回收机制
public class Mythred02 throws EXception{
    public static void main(String[] args){
        T t = new T();
        t.setDaemon(true);
        t.start;
        System.out.println("子线程为守护线程");
        for(int i=1;i<=20;i++){
        Thread.sleep(1000);
        System.out.println("主线程执行"+i+"次");
        }

}
class T extends Thread{
    public void run(){
        while(true){
        try{
        Thread.sleep(1000);
        }catch(InterruptedException e){
        e.printStackTrace();
        }
        System.out.println("子线程执行"+i+"次");
        }
    }
}

2、注意事项和细节

1、start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
2、线程优先级的范围
3、interrupt,中断线程,但并没有真正的中断线程,所以一般用于中断正在休眠线程
4、sleep,线程的静态方法,使当前线程休眠。

六、线程的七大状态

NEw:创建
Ready:就绪
Running:运行
TimedWaiting:休眠
Waiting:等待
Blocked:锁
Teminated:终止

七、线程同步机制

Synchronized

线程同步机制:

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

同步具体方法-Synchronized
同步代码块

synchronized(对象){ //得到对象的锁,才能操作同步代码
    //需要被同步代码
}

synchronized还可以放在方法声明中,表示整个方法为同步方法

public sychronized void m(String name){
    //需要被同步代码块
}

案例

public class TestSell{
    public static void main(String []args){
        Thread01 t= new Thread01;
        new Thread1(t).start();
        new Thread2(t).start();
        new Thread3(t).start();
    }
}

class Thread01 implements Runnable(){
    private int ticketNum = 50;
    private boolean loop = true;

    public synchronized void sell(){  //同步方法
        if(ticketNum <=0){
            System.out.println("售票结束");
            loop = false;
        }
        try{

            Thred.sleep(50);
        }catch(Exception e){
            e.printStackTrace();
        }
        System.out.println(Thread.getCurruentThread()+"售出一张票")
    }

    public void run(){
        while(loop){
            sell();
        }
    }
}

互斥锁

  1. 在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
  2. 每个对象都应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只有由一个线程来访问该对象
  3. 关键字synchronized来与对象的互斥锁进行联系,当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问该对象。
  4. 同步的局限性:导致程序的执行效率要降低
  5. 同步方法(非静态的)锁可以是this,也可以是其他对象(要求是同一个对象)
  6. 同步方法(静态的)为当前类本身,锁是加在类名.class

案例

public class TestSell{
    public static void main(String []args){
        Thread01 t= new Thread01;
        new Thread1(t).start();
        new Thread2(t).start();
        new Thread3(t).start();
    }
}

class Thread01 implements Runnable(){
    private int ticketNum = 50;
    private boolean loop = true;

    public  void sell(){
        sychronized(this){  //同步代码块
            if(ticketNum <=0){
                System.out.println("售票结束");
                loop = false;
            }
            try{
                Thred.sleep(50);
            }catch(Exception e){
                e.printStackTrace();
            }
            System.out.println(Thread.getCurruentThread()+"售出一张票")
            }
    }

    public void run(){
        while(loop){
            sell();
        }
    }
}

注意事项和细节

  • 同步方法如果没有使用static修饰,默认锁对象为:this
  • 如果同步方法使用static,默认锁对象为:当前类.class
  • 实现的落地步骤:
    需要先分析上锁的代码
    选择同步代码块或同步方法
    要求多个线程的锁对象为同一个即可

线程死锁

多个线程都占用了对方的资源,但不肯相让,导致了死锁,在编程时一定要避免死锁的发生。

public class TestLock{
    public static void main(String []args){
        DeadLockDemo d1 = new DeadLockDemo(true);
        DeadLockDemo d2 =new DeadLockDemo(false);
        d1.start;
        d2.start;
    }
}

class DeadLockDemo extends Thred{
    static Object o1 = new Object();
    static Object o2 = new Object();
    boolean flag;

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

    public void run(){
    //业务逻辑分析
    //1.如果flag为T,线程A就会得到o1对象锁,然后去尝试去获取o2对象锁
    //2.如果A线程得不到o2对象锁,就会blocked
    //3.如果flag为F,线程A就会得到o2对象锁,然后去尝试去获取o1对象锁
    //4.如果A线程得不到o1对象锁,就会blocked
        if(flag){
            synchronized(o1){
                System.out.println(Thread.currentThred.getName()+"进入1");
                synchronized(o2){
                    System.out.println(Thread.currentThred.getName()+"进入2");
                }
            }
        }else{
            synchronized(o2){
                System.out.println(Thread.currentThred.getName()+"进入3");
                synchronized(o1){
                    System.out.println(Thread.currentThred.getName()+"进入4");
                }
            }
        }
    }
}

留言