线程

线程

程序:完成特定任务,用某种语言编写的一段静态代码

进程:正在运行的程序,进程作为资源分配的单位,系统在运行时为每个进程分配了不同的内存域

线程:程序内部的一条执行路径,线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器

并行:多个CPU执行不同的任务,例如多个人执行不同的任务

并发:一个CPU(采用时间片的方式)同时执行多个不同的任务,多个人做同一件事,比如一千个人秒杀一台手机。


多线程的创建方式(四种)
①继承Thread类

​ 1.创建一个继承于Thread类的子类

​ 2.在子类中重写Thread的run方法,该方法里面写线程执行的操作

​ 3.创建该子类的对象

​ 4.用该子类对象调用start()方法,该方法是父类中的方法

public class Thread1Test {
    public static void main(String[] args) {
        /**
         * 一个已经执行start的线程不能用同一个线程启动第二次,需要新创建一个线程来启动
         * */
        MyThread1 myThread1=new MyThread1();
        MyThread1 myThread2=new MyThread1();
        myThread1.start();
        myThread2.start();
        System.out.println("hello");
    }
}
class MyThread1 extends Thread{
    /**
     * 线程逻辑业务
     * */
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

Thread里面的方法

①run():线程子类重写Thread的方法,该方法里面放线程里面需要执行的逻辑业务

②start():线程子类对象启动线程的方法

③currentThread():静态方法,获取当前线程

④getName():获取当前线程的名字

⑤setName():设置当前线程的名字

⑥yield():释放当前cpu执行该线程的执行权

⑦join():在线程a中调用线程b的方法,此时线程a进入了阻塞状态,只有等线程b执行完后,线程a才 阻塞。

⑧sleep():让进程休眠一段时间,进入阻塞状态

public class ThreadMethodTest {
    public static void main(String[] args) {
        MyThread12 myt1=new MyThread12();
        MyThread13 myt2=new MyThread13();
        myt1.setName("线程一");
        myt2.setName("线程二");
        myt1.start();
        myt2.start();
        for (int i = 0; i <50 ; i++) {
            /**
             * 调用join方法
             * */
            if(i==20){
                try {
                    myt1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if(i==30){
                try {
                    myt2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("主线程*********"+i);
        }
    }
}
class MyThread12 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            /**
             * 调用sleep方法,让进程进入阻塞状态
             * */
            if(i==50){
                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if(i%2!=0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
class MyThread13 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <50 ; i++) {
            if(i%2==0){
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

线程调度
调度策略

①时间片

②抢占式:高优先级的线程抢占CPU

线程优先级等级

MAX_PRIORITY:10

NORM_PRIORITY:5

MIN_PRIORITY:1

线程优先级的获取和设置

getPriority():Thread.currentThread.getPriority()

setPriority()


②实现Runnable接口

​ 1.创建一个实现runnable接口的类

​ 2.重写接口中的run()方法

​ 3.创建线程子类的对象

​ 4.将该对象以参数的形式传入Thread构造器当中,并创建Thread对象

​ 5.用Thread对象调用start()方法

public class Thread2Test {
    public static void main(String[] args) {
        //第三步:创建线程子类对象
        WindowSail win1=new WindowSail();
        //第四步:创建Thread对象,并将线程子类对象以参数形式传入Thread构造器中
        Thread th1 = new Thread(win1);
        Thread th2 = new Thread(win1);
        th1.setName("窗口1");
        th2.setName("窗口2");
        th1.setPriority(Thread.MAX_PRIORITY);
        //第五步:调用start方法
        th1.start();
        th2.start();
    }
}
//第一步:实现runnable接口
class WindowSail implements Runnable{
    private int ticket=100;
    //第二步:重写run方法
    @Override
    public void run() {
       while (true){
           if(ticket>0){
               System.out.println(Thread.currentThread().getName() + "取票:" + ticket);
               ticket--;
           }else {
               break;
           }
       }
    }
}

Thread.class
//run()方法
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

//Thread中的构造方法
public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

比较创建线程的两种方式

开发中:优先选择→实现Runnable接口的方式

​ 原因:①、实现方式没有类的单继承的局限性

​ ②、实现的方式更适合来处理多个线程有共享数据的情况

联系:都用到了Thread类,并且Thread实现了Runnable接口

​ 两种方式都需要重写run()方法,将线程要执行的逻辑声明在run()中


③实现Callable

​ 1)相比Runnable于run方法,该重写方法call()有返回值

​ 2)可以抛异常,被外面的操作捕获

​ 3)可以支持泛型

public class CallableThreadTest {
    public static void main(String[] args) {
        //3.创建Callable实现类的对象
        CallableWindow calw=new CallableWindow();
        //4.创建FutureTask对象,并将实现类对象以参数形式传入FutureTask的构造器中
        FutureTask futureTask1 = new FutureTask(calw);
        FutureTask futureTask2 = new FutureTask(calw);
        //5.将FutureTask对象以参数形式传入Thread构造器中,并调用Start方法
        new Thread(futureTask1).start();
        new Thread(futureTask2).start();
        try {
            //get获得的返回值就是FutureTask构造器参数Callable实现类重写方法call的返回值
            Object o = futureTask1.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
//1.创建实现Callable接口的实现类
class CallableWindow implements Callable{
    private int ticket=100;
    //2.重写call方法
    @Override
    public Object call() throws Exception {
        while (true) {
            synchronized(this){
                if(ticket>0){
                    System.out.println(Thread.currentThread().getName() + "正在出票" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
        return null;
    }
}

④创建线程池方式
public class ThreadPoolTest {
    public static void main(String[] args) {
        //1. 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable

//        service.submit(Callable callable);//适合使用于Callable
        //3.关闭连接池
        service.shutdown();
    }
}

线程生命周期

EL1K8S1XVK4SBFHH

解決线程安全的方法→同步机制
方式一:同步代码块
synchronzied(同步监视器){

//需要被同步的代码

}

说明:1。操作共享数据的代码,即为需要被同步的代码

​ 2.共享数据:多个线程共同操作的变量。比如抢票中的车票就是共享数据

​ 3.同步监视器,俗称锁,任何一个类的对象,都可以充当锁。

​ 要求:多个线程必须要使用同一把锁,也就是用作锁的实例对象只能创建一次

public class ThreadSynchronized {
    public static void main(String[] args) {
        WindowSynchronized winS11=new WindowSynchronized();
        winS11.start();
        WindowSynchronized winS12=new WindowSynchronized();
        winS12.start();
        //*****
        WindowSynchronized winS2=new WindowSynchronized();
        Thread thread1=new Thread(winS2);
        Thread thread2=new Thread(winS2);
        thread1.start();
        thread2.start();
    }
}
class WindowSynchronized extends Thread{
    private static int ticket=100;
    /**
     * 这里由于继承Thread类方式如果多线程调用的话是创建多个线程子类对象,那么object就不唯一了
     * 而锁是需要唯一,因此需要用static,这个锁可以是任何一个对象,并不一定要object比如可以创
     * 建一个Dog对象
     * */
    private static Object object=new Object();
    @Override
    public void run() {
        while (true){
            /**
             * 这里除了可以用object这个锁之外,还可以用WindowSynchronized.class,这里用到了
             * 反射知识,Class clazz=WindowSynchronized.class,这也是个对象,即类本身也是对
             * 象
             * synchronized(WindowSynchronized.class){}
             * */
            synchronized (object){
                if(ticket>0){
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}
/**
* 实现Runnable接口方式
* */
class WindowSynchronized2 implements Runnable{
    private int ticket=100;
    /**
     * 这里不用static是由于在调用时候只创建了WindowSynchronized2一次对象,所以对象锁是唯一的
     * */
    private Object object=new Object();
    @Override
    public void run() {
        while (true){
            /**
             * 这里也可以用object方式
             * */
            synchronized (this){
                if(ticket>0){
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}
方式二:同步方法

1.同步方法仍然需要用到同步监视器,知识不需要我们显示的声明

2.非静态的同步方法,同步监视器是this

​ 静态的同步方法,同步监视器是当前类本身

public class SynchronizedMethod {
    public static void main(String[] args) {
        WindowRunnable winr=new WindowRunnable();
        Thread th1=new Thread(winr);
        Thread th2=new Thread(winr);
        th1.start();
        th2.start();
        //Thread方法
        WindowThread wint=new WindowThread();
        WindowThread wint1=new WindowThread();
        wint.start();
        wint1.start();
    }
}
/**
 * 实现Runnable方式的同步方法
* */
class WindowRunnable implements Runnable{
    private int ticket=100;

    @Override
    public void run() {
        while (true){
            sail();
        }
    }
    /**
     * 这里的同步监视器(锁)就是this,当前对象
     * */
    private synchronized void sail(){
        if(ticket>0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + ticket);
            ticket--;
        }
    }
}
/**
 * 继承Thread类的方式
 * */
class WindowThread extends Thread{
    private static int ticket=100;

    @Override
    public void run() {
        while (true){
            sail();
        }
    }
    /**
     * 这里的同步监视器是WindowThread.class类对象,而且这里一定要有static
     * */
    private static synchronized void sail(){
        if(ticket>0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + ticket);
            ticket--;
        }
    }
}

方式三:Lock锁

class WindowLock implements Runnable{
    private int ticket=100;
    private ReentrantLock lock=new ReentrantLock();
    //第二步:重写run方法
    @Override
    public void run() {
        while (true){
            /**
             * 这个lock一定是紧跟try前面,并且调用lock方法之后一定要调用unlock方法,并且
             * 这个unlock方法一定是在finally第一行
             * */
            lock.lock();
            try {
                if(ticket>0){
                    if(ticket==20){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "取票:" + ticket);
                    ticket--;
                }else {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}
同步机制解决单例模式(懒汉式)线程安全
class Bank{
    private static Bank instance=null;
    /**
     * 同步代码块方式
     * */
    public static Bank getInstance(){
        if(instance==null){
            synchronized (Bank.class){
                if(instance==null){
                    instance=new Bank();
                }
            }
        }
        return instance;
    }
    /**
     * 同步方法方式
     * */
    public static synchronized Bank getInstance1(){
        if(instance==null){
            instance=new Bank();
        }
        return instance;
    }
}
线程通信间使用到的两个方法
1.wait()

​ 一旦调用了wait方法,那么当前线程就会进入阻塞状态,并且释放同步监视器(锁)

​ 调用notify()方法会唤醒被wait的线程,如果多个线程被wait(),那么会唤醒优先级高的那个

​ 调用notifyAll()方法会唤醒所有被wait的方法

2.sleep()

​ 一旦执行此方法,线程会进入阻塞状态

wait()和slee()方法的异同

同:这两个方法都可以使当前线程进入阻塞状态

不同:1)两个方法的位置不同,wait()是在java.lang.Object中,而sleep()是在Thread中。

​ 2)调用的要求不同,sleep可以在任何场景下被调用,但是wait()只能在同步代码或者同步方法

​ 中被调用

​ 3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep不会释放 同步监视器,而wait()一旦被调用就会释放当前线程的所拥有的锁,被其它线程所获得,其它 线程调用notify后会将wait的线程所唤醒,结束阻塞状态。

生产者消费者模型
public class ProductConsumerTest {
    public static void main(String[] args) {
        Product product=new Product();
        Producer producer=new Producer(product);
        Consumer consumer=new Consumer(product);
        Thread thpro=new Thread(producer);
        Thread thcon=new Thread(consumer);
        thpro.setName("生产者");
        thcon.setName("消费者");
        thpro.start();
        thcon.start();
    }
}
class Product{
    private int productor=0;
    public synchronized void productShow() {
        if(productor<20){
            productor++;
            System.out.println(Thread.currentThread().getName() + "在生产第" + productor + "个产品");
            //唤醒消费者,说明可以开始消费了
            notify();
        }else {
            /**
             * 这里是当生产者生产的产品超过了20个,那么就要进入阻塞状态,不能继续生产了,只有等着
             * 消费者消费一个之后那么就还可以生产,由于此时生产者进入了阻塞状态,那么只有靠消费者
             * 调用notify来唤醒生产者,反过来,生产者唤醒消费者同样原理
             * */
            try {
                wait(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void consumerShow() {
        if(productor>0){
            System.out.println(Thread.currentThread().getName() + "正在消费第" + productor + "个产品");
            productor--;
            notify();
        }else {
            try {
                wait(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Producer implements Runnable{
    Product product;

    public Producer(Product product) {
        this.product = product;
    }

    @Override
    public void run() {
        System.out.println("生产者开始生产产品了");
        while (true) {
            product.productShow();
        }
    }
}
class Consumer implements Runnable{
    Product product;

    public Consumer(Product product) {
        this.product = product;
    }

    @Override
    public void run() {
        System.out.println("消费者开始消费产品了");
        while (true){
            product.consumerShow();
        }
    }
}
死锁问题

多线程间都在等待对方的和自己的同步资源,它不会报异常,只是所有线程都处于阻塞状态。

解决方法

​ 1.专门的算法、原则

​ 2.尽量减少同步资源的定义

​ 3.尽量避免嵌套同步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值