08.JAVA线程基础

1.线程介绍

单线程:同一个时刻,只允许执行一个线程

多线程:同一个时刻,允许执行多个线程

并发:同一时刻,多个任务交替执行。

并行:同一时刻,多个任务同时执行。

2.线程使用

方式一:继承Thread类,重写run方法

public class thread01 {
    public static void main(String[] args) {
        //创建Cat对象,可以当作线程使用
        Cat cat = new Cat();
        cat.start();//启动线程-->最终会执行cat的run方法
        //主线程main和子线程cat会交替执行,主线程不会阻塞
        //如果这里还有代码,也会执行,不是等到子线程执行完,才执行这里的代码
    }
}
class Cat extends Thread{
    //1.当一个类继承了thread类,该类就是线程类
    //Thread的run方法是实现Runnable接口的run方法
    int times=0;
    @Override
    public void run() {//重写run方法
        while (true){
            System.out.println("喵喵"+(++times)+"线程名称="+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);//毫秒=1秒
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(times == 80){
                break;
            }
        }
    }
}

方式二:实现Runable接口,重写run方法(静态代理)

例1:Thread类代理

public class runnable02 {
    public static void main(String[] args) {
        T2 t2 = new T2();
        //这里不能直接调用star
        //创建一个Thread对象,把t2对象传入
        Thread thread=new Thread(t2);
        //底层是静态代理模式
        thread.start();
    }
}
class T2 implements Runnable{
    int count=0;
    @Override
    public void run() {
        while (true){
            System.out.println("修狗"+(count++));
            //休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

例2:Cat1类模拟Thread类的静态代理

public class runable {
    public static void main(String[] args) {
        Cat1 cat1=new Cat1();
        new Proxy(cat1).start();
    }
}
class Animal{}
class Cat1 extends Animal implements Runnable{

    @Override
    public void run() {
        System.out.println("猫咪");
    }
}
//模拟的Thread
class Proxy implements Runnable{
    private Runnable target=null;
    @Override
    public void run() {
        if (target!=null){
            target.run();
        }
    }

    public Proxy(Runnable target) {
        this.target = target;
    }
    public void start(){
        start0();
    }
    public void start0(){
       run();
    }
}

图竖着看 

Thread和Runnable 本质上没有区别,都是start()-->start0()-->run()。

Runnable接口更适合多个线程共享一个资源,避免了单继承的情况。

T3 t3 = new T3();
Thread thread1 = new Thread(t3);
Thread thread2 = new Thread(t3);
thread1.start();
thread2.start();

如何通知线程停止?

public class Thread03 {
    public static void main(String[] args) throws InterruptedException {
        T3 t3 = new T3();
        Thread thread1 = new Thread(t3);
        thread1.start();
        Thread.sleep(10 * 1000);//主线程休眠10秒,子线程还在一直跑
        t3.setLoop(false);//10秒之后,主线程恢复,通知子线程停止

    }
}
class T3 implements Runnable{
    private boolean loop=true;
    int n=10;
    @Override
    public void run() {
        while (loop){
            System.out.println("hello world"+(n++));
            try {
                Thread.sleep(1000);//子线程每隔1秒输出一次
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

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

3.线程方法

常用方法第一组:
1.setName ()//设置线程名称,使之与参数 name 相同
2. getName ()//返回该线程的名称
·3.start() //使该线程开始执行; Java 虚拟机底层调用该线程的
4.run ()//调用线程对象 run 方法
5.setPriority() //更改线程的优先级
6.getPriority() //获取线程的优先级
7. sleep()//在指定的毫秒数内让当前正在执行的线程休眠
8. interrupt()//中断线程

public class ThreadMothd01 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setName("qwq");
        t.setPriority(Thread.MAX_PRIORITY);
        t.start();

        //主线程打印5 hi,然后我就中断 子线程休眠
        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);
            }
            System.out.println(Thread.currentThread().getName()+"休眠---");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"唤醒了---");
        }

    }
}

 常用方法第二组:
1. yield():线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功。
2.join(): 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
案例: 创建一个子线程 ,每隔1s 输出 hello,输出 20次,主线程每隔1秒,输出 hi,输出 20次.要求: 两个线程同时执行,当主线程输出 5次后,就让子线程运行完毕,主线程再继续。

public class TreadMothed01 {
    public static void main(String[] args) throws InterruptedException {
        Hi hi = new Hi();
        hi.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("hi");
            Thread.sleep(500);
            if(i==4){
                System.out.println("第五个");
                hi.join();
            }
        }
    }
}
class Hi extends Thread{
    private int n=0;

    @Override
    public void run() {
        while (true){
            System.out.println("hello");
            n=n+1;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(n==20){
                break;
            }
        }

    }
}

 用户线程和守护线程 

1.用户线程:也叫工作线程,当线程的任务执行完结束或通知方式结束。
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。

常见的守护线程: 垃圾回收机制。

如何设计一个守护线程?

答://先设置成守护线程,再启动,否则报错
hi.setDaemon(true);

public class TreadMothed01 {
    public static void main(String[] args) throws InterruptedException {
        Hi hi = new Hi();
        //如果我们希望主线程结束,子线程自动结束
        //把子线程设计成守护线程
        //先设置成守护线程,再启动,否则报错
        hi.setDaemon(true);
        hi.start();
        for (int i = 1; i <= 10; i++) {
            System.out.println("海贼王qwq");
            Thread.sleep(1000);
        }

    }
}
class Hi extends Thread{
    private int n=0;

    @Override
    public void run() {
        while (true){
            System.out.println("hello");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

4.线程生命周期

5.Synchronized

使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。 

具体实现方法-synchronized:

1.同步代码块
synchronized(对象){ // 得到对象的锁,才能操作同步代码
// 需要被同步代码;}
2.同步方法
public synchronized void m (String name)!
//需要被同步的代码

 注意事项和细节:
1.同步方法如果没有使用static修饰: 默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤
(1).需要先分析上锁的代码
(2).选择同步代码块或同步方法
(3).要求多个线程的锁对象为同一个即可!

public class spiao {
    public static void main(String[] args) {
        per per = new per();
        new Thread(per).start();
        new Thread(per).start();
        new Thread(per).start();


    }
}
//实现接口方式,使用synchronized实现线程同步
class per implements Runnable {//创建Runnable接口的实现类
    private int ticket = 50;
    private static boolean lop=true;//控制run方法变量
    Object o = new Object();

    //同步方法:静态
    //1.public synchronized static void m() {}
    //2.锁在per.class
    //3.也可以在代码块上写 synchronized ,同步代码块,锁是在类上
    public synchronized static void m1() {

    }
    public static void m2() {
        synchronized (per.class){
            System.out.println("aa");
        }
    }
    //同步方法:非静态
    //1.public synchronized void m() {}是一个同步方法
    //2.这时锁在 this 对象上
    //3.也可以在代码块上写 synchronized ,同步代码块,锁还是在this对象上
    public /*synchronized*/ void m() {//同一时刻,只有一个线程执行m方法
        synchronized (/*this*/ o) {
            if (ticket <= 0) {
                System.out.println("售票结束");
                lop = false;
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口还有" + (ticket--) + "票");
        }
    }

    @Override
    public void run() {
        while (lop) {
            m();
        }
    }
}

6.互斥锁

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

7.死锁

拿到a锁,才能拿b锁,然后执行代码

拿到b锁,才能拿a锁,然后执行代码

执行2个线程,一个拿到a锁,一个拿到b锁,都拿不到剩下的锁,就形成了死锁。

释放锁

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

8.练习 

1.多线程执行

请编写一个程序,创建两个线程,一个线程每隔1秒输出“hello,world”,输出10
次,退出,一个线程每隔1秒输出“hi”,输出 5次退出。

public class Thread03 {
    public static void main(String[] args) {
        T3 t3 = new T3();
        T4 t4 = new T4();
        new Thread(t3).start();
        new Thread(t4).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程。。不会阻塞");
        }

    }
}
class T3 implements Runnable{
    int n=10;
    @Override
    public void run() {
        while (true){
            System.out.println("hello world"+(n++));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (n==10){
                break;
            }
        }
    }
}
class T4 implements Runnable{
    int m=5;
    @Override
    public void run() {
        while (true){
            System.out.println("hi"+(m++));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (m==5){
                break;
            }
        }
    }
}

2.

(1)在main方法中启动两个线程
(2)第1个线程循环随机打印100以内的整数
(3) 直到第2个线程从键盘读取了“Q”命令

public class e1 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B(a);//传Q入a才能控制a
        a.start();
//输完Q之后按回车,才会把Q传给k
        b.start();

    }
}
class A extends Thread{
    private  boolean loop=true;

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

    @Override
    public void run() {
        while (loop){
            System.out.println( (int) (Math.random()*100) + 1 );
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
class B extends Thread{
    private A a;
    private Scanner scanner = new Scanner(System.in);
   public B(A a){
       this.a=a;
   }
    @Override
    public void run() {
        while (true){
            System.out.println("请输入字符。。。");
            char k = scanner.next().toUpperCase().charAt(0);
            if(k == 'Q'){
                a.setLoop(false);//a线程退出
                break;//b线程也退出
            }
        }
    }
}

3.

2个人同时取10000块钱,每次取1000,不能超取

public class e2 {
    public static void main(String[] args) {
        User user = new User();
        new Thread(user).start();
        new Thread(user).start();
    }
}
class User implements Runnable{
    private int num=10000;

    @Override
    public  void run() {
        while (true){
            synchronized (this){//那个线程获取到this锁,就执行代码块
                //得不到的就阻塞
                if(num>=1000){
                    num=num-1000;
                    System.out.println(Thread.currentThread().getName()+"取1000"+"当前余额"+num);
                }else {
                    System.out.println("没钱了");
                    break;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值