自学内容网 自学内容网

java-----多线程

多线程基本概念

1.什么是多线程?

        有了多线程,我们就可以让程序同时做多件事情

2.多线程的作用?

        提高效率,让cpu在多个程序之间切换

3.多线程的应用场景?

        只要你想让多个事情同时运行就需要多线程

        比如:软件中的耗时操作、所有聊天软件、所有的服务器

并发和并行

1.并发:在同一时刻,有多个指令在单个CPU上交替执行

2.并行:在同一时刻,有多个指令在多个CPU上同时执行

多线程三种实现方式

 1.继承Thread类的方式实现

        将一个类声明为Thread的子类。 这个子类应该重写Thread类的方法run。

代码块演示:

        

public class MyThread extends Thread{
    //子类调用父类的getName方法
    @Override
    public void run() {//t1 t2都会执行当前
        //线程要执行的
        for (int i = 0; i < 20; i++) {
            Thread thread = Thread.currentThread();
            System.out.println(getName()+"helloworld");
        }
    }
}

测试类main方法:

public class demo1{
    public static void main(String[] args) {
        //1.自己定义一个类继承Thread
        //2.重写run方法
        //3.创建子类对象,并启动线程

        MyThread t1=new MyThread();
        MyThread t2=new MyThread();

        t1.setName("线程1");
        t2.setName("线程2");
        //启动线程
        t1.start();
        t2.start();

    }
}

运行结果:仅仅是一部分运行结果,可以看出线程1和2是交替执行的

 2.实现Runnable接口的方式进行实现

          创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。

代码演示:

public class MyRun implements Runnable{
    //自强
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Thread thread = Thread.currentThread();//获取当前线程
            System.out.println(thread.getName()+"nihao");
        }
    }
}
public class demo2 {
    public static void main(String[] args) {
        //1.自己定义一个类实现Runnable接口
        //2.重写里面的run方法
        //3.创建自己类的对象
        //4.创建一个Thread对象,并开启线程
        MyRun mr=new MyRun();
        Thread t1=new Thread(mr);
        Thread t2=new Thread(mr);
        t1.setName("线程1");
        t1.start();

        t2.setName("线程2");
        t2.start();
    }
}

 3.利用Callable接口和Future接口方式实现

            可以获得多线程执行的返回结果

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 0; i <=100; i++) {
            sum+=i;
        }
        return sum;
    }
}
public class demo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //可以获取多线程运行的返回结果
        //1.创建MyCallable对象 表示多线程要执行人物
        MyCallable mc=new MyCallable();
        //创建FutureTask 管理结果
        FutureTask<Integer> ft=new FutureTask<>(mc);
        Thread t1=new Thread(ft);
        t1.start();
        Integer result=ft.get();
        System.out.println(result);
    }
}

运行结果:

5050

 多线程中常见成员方法

部分代码演示: 

 线程优先级:

        抢占式调度:随机性、优先级越大,线程抢占概率越大

        

public class demo11 {
    public static void main(String[] args) {
        MyThread4 t1=new MyThread4("坦克");
        MyThread4 t2=new MyThread4("飞机");
        System.out.println(t1.getPriority());
        System.out.println(t2.getPriority());
        t1.setPriority(1);
        t2.setPriority(10);//最大优先级为10
        t1.start();
        t2.start();
    }
}
public class MyThread4 extends Thread{
    public MyThread4(String name){
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+"-"+i);
        }
    }
}
执行结果:
飞机和坦克都可能先执行完毕,优先级不是绝对只是概率问题

守护线程:

        当非守护线程执行完毕后,守护线程就会结束。

应用场景:

        比如qq两天,其中一个要发送文件给另一个人,聊天是线程1,传输文件时线程2,当线程1执行结束后,那么线程2也没有存在的必要了,线程2可以设置为守护线程。

        

线程生命周期 

 线程安全问题

  应用场景:

        某电影院目前正在上映国产大片,共有100张票,而它又3个窗口买票,设计一个程序模拟电影院买票。

        

public class demo7 {
    public static void main(String[] args) {
        ThreadMyRun tmr=new ThreadMyRun();
        Thread t1=new Thread(tmr);
        Thread t2=new Thread(tmr);
        Thread t3=new Thread(tmr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:会发现出现重复的票,也会有超出范围的票

原因:

1.打印会出现重复的票:

        三个线程,第一个线程执行完之后,会睡眠10毫秒,然后线程2这时候会抢占进程,然后睡眠10毫秒,然后第三个线程执行完之后,会睡眠10毫秒,一直在ticket++,最后都输出卖出ticket张票,所以这里出现了打印出现重复的票.

2.超出范围的票

        当打印到99的时候,三个线程出现上面的情况,因为一直没有打印完,ticket++了三次,所以打印完之后才会出现再去判断票是否还满足100以内.打印了102、102、102

怎么解决这个问题?

        当有线程在代码中执行时,就把它锁起来,只有当前线程执行完之后,其它线程才能进入,把共享数据的这段代码锁起来

同步代码块

        把操作共享数据的代码锁起来

格式:

        synchronized(锁){

                操作共享数据的代码。

}

特点1:锁默认打开,有一个线程进去了,锁自动关闭

特点2:里面的代码全部执行完毕,线程出来,锁自动打开

public class ThreadMyRun implements Runnable{
    int ticket=0;//这种创建方法 因为ThreadMyRun是传递的参数 只会创建一次
    @Override
    public void run() {
        while(true){
            try {
                if (method()) break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private boolean method() throws InterruptedException {
        synchronized (ThreadMyRun.class){ //1.不能写在while循环的外面,因为如果放在外面,只有第一个抢到运行优先权线程执行完while之后,才能第二个线程进入同步代码块
2.同步代码块里的锁必须时唯一的,这里用的字节码文件,如果用几个线程用不同的锁,那么锁的意义也就没了
            if(ticket==100){
                return true;
            }
            else{
                Thread.sleep(100);
                ticket++;
                System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"票");
            }
        }
        return false;
    }
}

运行结果:可以看到正常运行

lock锁

提供了获得锁和释放锁的方法:lock()、unlock()

public class LockDemo extends Thread{
    static int ticket=0;
    static Lock lock=new ReentrantLock();//因为Lock是接口,需要创建实现类对象
    @Override
    public void run() {
        lock.lock();//加锁
        while(true){
            if(ticket==100){
                break;
            }
            else{
                try {
                    Thread.sleep(1000);
                    ticket++;
                    System.out.println(getName()+"正在卖第"+ticket+"张票");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();//执行完毕之后释放锁
                }
            }
        }
    }
}

死锁

死锁是一种常见的问题,它发生在多个线程相互等待对方释放资源时,导致它们都无法继续执行。

一般锁的嵌套会出现这种问题。

生产者消费者问题

代码模拟: 

public class Cook extends Thread{
    @Override
    public void run() {
        while(true){
            synchronized (Desk.lock){
                if(Desk.count==0){
                    break;
                }
                else{
                    if(Desk.foodFlag==1){
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else{
                        System.out.println("厨师又做了一碗,还有");
                        Desk.foodFlag=1;
                        Desk.lock.notifyAll();//等待消费者
                    }
                }
            }
        }
    }
}
public class Foodie extends Thread{
    @Override
    public void run() {
        /*
        * 1.循环
        * 2.同步代码块
        * 3.共享数据是否到了末尾 到了末尾
        * 4.未到末尾
        * */
        while(true){
            synchronized (Desk.lock){
                if(Desk.count==0){
                    break;
                }
                else{
                    if(Desk.foodFlag==0){//没有面条
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else{
                        Desk.count--;
                        System.out.println("吃过正在吃面条,还能再吃"+Desk.count+"碗");
                        Desk.lock.notifyAll();//唤醒的是厨师
                        Desk.foodFlag=0;
                    }
                }
            }
        }
    }
}
package ThreadDemo;

public class Desk {
    /*
    控制生产者消费者的执行
     */
    //是否有面条 0有 1没有
    public static int foodFlag=0;
    //总个数
    public static int count=10;
    //上锁
    public static Object lock=new Object();

}
public class demo9 {
    public static void main(String[] args) {
        Cook c=new Cook();
        Foodie f=new Foodie();
        c.setName("厨师");
        c.setName("吃货");

        c.start();
        f.start();
    }
}
运行结果:

生产者消费者阻塞队列实现

public class Cook2 extends Thread{
    ArrayBlockingQueue<String> queue;
    public Cook2(ArrayBlockingQueue<String>queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        while(true){
            try {
                queue.put("面条");
                System.out.println("厨师放一碗面条");//这个是锁的外面
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
public class Food2 extends Thread{
    ArrayBlockingQueue<String> queue;
    public Food2(ArrayBlockingQueue<String>queue){
        this.queue=queue;
    }
    @Override
    public void run() {
        while(true){
            try {
                String food=queue.take();
                System.out.println(food);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
    }
}
public class demo10 {
    public static void main(String[] args) {
        ArrayBlockingQueue<String>queue=new ArrayBlockingQueue<>(1);//创建容量为1的阻塞队列

        Food2 food2=new Food2(queue);
        Cook2 cook2=new Cook2(queue);

        food2.start();
        cook2.start();
    }
}

线程6种状态


原文地址:https://blog.csdn.net/qq_44766305/article/details/143033808

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!