WGL的day17

多线程

◆说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运
行的含义,是一个静态的概念。
◆而进程则是执行程序的- -次执行过程,它是一个动态的概念。是系统资源分配的单

◆通常在一个进程中可以包含若干个线程,当然一个进程中至少有一 个线程,不然没
有存在的意义。线程是CPU调度和执行的的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务
器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能
执行一个代码,因为切换的很快,所以就有同时执行的错觉。

核心概念:

◆线程就是独立的执行路径;
◆在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
◆main()称之为主线程,为系统的入口,用于执行整个程序;
◆在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与
操作系统紧密相关的,先后顺序是不能认为的干预的。
◆对同- -份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
◆线程会带来额外的开销,如cpu调度时间,并发控制开销。
◆每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

三种创建线程的方式

Thread class 继承Thread类(重点)

Runnable接口 实现Runnable接口(重点)

Callable接口 实现Callable接口(了解)

在这里插入图片描述

package XC;

public class Demo1 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i= 0;i < 20 ; i++){
            System.out.println("我在看代码---"+i);
        }
    }

    public static void main(String[] args) {
        //main线程,主线程
        Demo1 d = new Demo1();
        d.start();
         for (int i =0; i<20;i++){
             System.out.println("我在学习多线程-"+i);
         }

    }
}

Runnable接口

并发

package XC;
//多线程同时操作一个对象
//买火车票的例子
//发下问题,线程同步并发,容易出错
public class Demo2 implements Runnable{
    //票数
    private int tickets = 10;

    @Override
    public void run() {
        while (true) {
            if (tickets <= 0) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "拿到了第:" + tickets-- + "张票~");

        }
    }

    public static void main(String[] args) {
        Demo2 d = new Demo2();
        new Thread(d,"黄一").start();
        new Thread(d,"小二").start();
        new Thread(d,"张三").start();
    }
}

Lamda表达式

◆入希腊字母表中排序第十一位的字母,英语名称为L _ambda
◆避免匿名内部类定义过多
◆其实质属于函数式编程的概念
(params) -> expression [表达式]
(params) -> statement [语句]
(params) -> { statements }

a-> System. out.println("i like lambda-->"+a) ;
new Thread (()->System.out.println("多线程学习...")).start();

为什么要使用lambda表达式
◆避免匿名内部类定义过多
◆可以让你的代码看起来很简洁
◆去掉了- 堆没有意义的代码,只留下核心的逻辑。
◆也许你会说,我看了Lambda表达式,不但不觉得简洁,反而觉得更乱,看不懂了。那是因为我们还没有习惯,用的多了,看习惯了,就好了。

package XC;

public class Lambda {
    /*static class love implements ilove{
        public void love(int a) {
            System.out.println("i love you"+a);
        }*/
    public static void main(String[] args) {




         /*ilove il = new ilove() {
            @Override
            public void love(int a) {
                System.out.println("i love you"+a);
            }
        };
        il.love(5);*/
        ilove love = null;
        love = a->{
            System.out.println("i love you"+a);
        };
        love.love(521);
}

interface ilove{
    void love(int a);
}


}

线程状态

线程停止

在这里插入图片描述

◆不推荐使用JDK提供的stop()、(destroy()方法。[已废弃])
◆推荐线程自己停止下来建议使用一个标志位进行终止变量当flag=false,则终止线程运行。

package XC;

public class Demo3 implements Runnable{
    private boolean flag = true;
    @Override
    public void run() {
     int i =0;
     if (flag){
         System.out.println("run......"+i++);
     }
    }
    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        Demo3 d = new Demo3();
        new Thread(d).start();
        for (int i = 0;i<=1000;i++){
            System.out.println("main"+i);
            if (i==500){
                d.stop();
                System.out.println("线程停止了");
            }
        }
    }
}

线程休眠

◆sleep (时间)指定当前线程阻塞的毫秒数;
◆sleep存在异 常InterruptedException;
◆sleep时间达到后线程进入就绪状态;
◆sleep可以模拟网络延时,倒计时等。
◆每一个对象都有一个锁,sleep不会释放锁;

package XC;
//模拟倒计时
public class TestSleep2 {
    public static void main(String[] args) throws InterruptedException {
             tenDown();
    }

    public static void tenDown() throws InterruptedException {
        int num = 10;
        boolean flag = true;

        while (flag) {
            Thread.sleep(1000);
            System.out.println(num--);
            if (num < 0) {
                flag = false;
                System.out.println("计时结束");
            }
        }
    }
}

线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞

  • 将线程从运行态转为就绪态

  • 让cpu重新调度,礼让不一定成功!主要看cpu的调度

  • package XC;
    
    public class TestYield {
        public static void main(String[] args) {
            MyYield myYield = new MyYield();
            new Thread(myYield,"A").start();
            new Thread(myYield,"B").start();
    
        }
    
    
    }
    class MyYield implements Runnable{
    
        @Override
        public void run() {
            int i= 0;
            while(i<4)
                System.out.println(Thread.currentThread().getName() + "线程开始执行"+i++);
                Thread.yield();//礼让
                System.out.println(Thread.currentThread().getName() + "线程开始执行"+i++);
            }
        }
    

Join

  • Join合并线程,待此线程执行完成后,再执行其他线程其他线程阻塞(插队)
package XC;
//Join(插队)
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("VIP用户:"+i+"号");
        }
    }

    public static void main(String[] args) throws Exception{
        TestJoin tj = new TestJoin();
       Thread thread = new Thread(tj);
       thread.start();
        for (int i = 1; i <= 500; i++) {
            System.out.println("普通main用户:"+i+"号");
            if (i==100){
               thread.join();
            }
        }

    }
}

Thread .State

线程状态。线程 可以处于以下状态之一:
●NEW
尚未启动的线程处于此状态。
●RUNNABLE
在Java虛拟机中执行的线程处于此状态。
●BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
●WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
●TIMED WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
●TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。

package XC;
//观察测试线程状态
public class TestState {


    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            //睡眠5秒钟
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("-----");
        });
        //观察状态
        Thread.State state = thread.getState();
        System.out.println(state);//NEW

        //观察启动后的状态
        thread.start();
        state = thread.getState();
        System.out.println(state);//5秒等待,等待。。。

        while (state!= Thread.State.TERMINATED){//只要线程不终止,就一直是输出状态
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);

        }
    }

}

◆Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
◆线程的优先级用数字表示,范围从1~10.
◆Thread .MIN_ PRIORITY = 1;
◆Thread.MAX_ PRIORITY = 10;
◆Thread.NORM_ PRIORITY = 5;

◆使用以下方式改变或获取优先级
◆getPriority() . setPriority(int xxx)

优先级的设定建议在star()调度前优先级低只是意味着获得调度的概率低.并不是优先级低就不会被调用了.这都是看CPU的调度

package XC;

public class TestPriority {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        t1.start();

        t2.setPriority(8);
        t2.start();

        t3.setPriority(5);
        t3.start();

        t4.setPriority(3);
        t4.start();

        t5.setPriority(Thread.MAX_PRIORITY);
        t5.start();

    }


    static class MyPriority implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
        }
    }
}

守护线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如后台记录操作日志,监控内存,垃圾回收等待
package XC;
//守护线程
//上帝守护你
public class TestDaemoin {
    public static void main(String[] args) {
        You you = new You();
        God god = new God();

        Thread thread = new Thread(god);
        thread.setDaemon(true);   //默认是false,因为正常线程都是用户线程
        thread.start();           //上帝收火狐线程启动
        new Thread(you).start();



    }

    //上帝
    static class God implements Runnable{
        @Override
        public void run() {
            while(true) {
                System.out.println("上帝守护你");
            }
        }
    }
    //你
    static class You implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 30000; i++) {
                System.out.println("开心的生活着~");
            }
            System.out.println("-----die----");
        }
    }
}

线程同步

同一个对象被多个线程调用

形成条件:队列+锁

◆由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题, 为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized ,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。

存在以下问题:
◆一个线程持有锁会导致其他所有需要此锁的线程挂起;
◆在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
◆如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置,引起性能问题.

银行取钱不安全案例
package XC;

import demo7.A;

public class UnsafeBank {


    public static void main(String[] args) {
        Account account = new Account(100,"结婚基金");
        Usemoney you = new Usemoney(account,50,"你");
        Usemoney girl = new Usemoney(account,100,"girl");
        you.start();
        girl.start();
    }
    }


    class Account{
        int money;
        String name;

        public Account(int money, String name) {
            this.money = money;
            this.name = name;
        }
    }



        class Usemoney extends Thread {
            //账户
            Account account;
            //取了多少钱
            int drawingmoney;
            //现在手里的钱
            int nowmoney;

            public Usemoney(Account account, int drawingmoney, String name) {
                super(name);
                this.account = account;
                this.drawingmoney = drawingmoney;
            }


            @Override
            public void run() {
                //如果没有钱
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (account.money - drawingmoney < 0) {
                    System.out.println(Thread.currentThread().getName() + "钱不够了");
                    return;
                } else {
                    nowmoney = nowmoney + drawingmoney;
                    account.money = account.money - drawingmoney;
                    System.out.println(account.name + "余额为:" + account.money);
                    System.out.println(this.getName() + "手里的钱:" + nowmoney);
                }
            }
        }
买票不安全案例
package XC;
//不安全的买票
public class UnsafeTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket,"黄牛").start();
        new Thread(ticket,"我").start();
        new Thread(ticket,"同学").start();


    }

    private static class Ticket implements Runnable{
        int tickets = 10;
        boolean flag = true;
        @Override
        public void run() {
            while (flag){
                buy();
            }

        }
        private void buy(){
           if (tickets<=0){
               flag = false;
               return;
           }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+tickets--+"张票");


        }
    }
}
Arraylist线程为什么不安全
package XC;

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList();

        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        //Thread.sleep(300);
        System.out.println(list.size());
    }
}

//结果小于10000

同步方法

◆由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块.

同步方法: public synchronized void method(int args) {}

◆synchronized方法控制对“对象”的访问,每个对象对应一-把锁 ,每个synchronized方法都必须获得调用该方的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的程才能获得这个锁,继续执行

缺陷:若将一个大的方法申明为synchronized将会影响效率。

◆同步块: synchronized (Obj ) { }
◆Obj称之为同步监视器
◆Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
◆同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this ,就是这个对象本身,或者是class [反射中讲解]
◆同步监视器的执行过程
1.第一个线程访问,锁定同步监视器,执行其中代码.
2.第二个线程访问 ,发现同步监视器被锁定, 无法访问.
3.第一个线程访问完毕,解锁同步监视器.
4.第二个线程访问, 发现同步监视器没有锁,然后锁定并访问

死锁

◆多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形.。某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。

package XC;

import java.util.List;

public class TestSynchronized {
    public static void main(String[] args) {
        Makeup m1 = new Makeup(0,"灰姑娘");
        Makeup m2 = new Makeup(1,"白雪公主");
        m1.start();
        m2.start();

    }

}
class Lipstick{

}
class Mirror{

}

class Makeup extends Thread{
    String name;
    int choice;

    //需要的资源只有一份用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();



    Makeup(int choice,String name){
        this.name = name;
        this.choice = choice;
    }
        
    
    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    private void makeup()throws InterruptedException{
        if (choice == 0){
            synchronized (lipstick){
                System.out.println(name+"抢到了口红锁");
            }
            Thread.sleep(2000);
            synchronized (mirror){
                System.out.println(name+"2秒后拿到镜子锁");//语句从线程中提出来
            }

        }
        else {
            synchronized (lipstick){
                System.out.println(name+"抢到了镜子锁");
            }
            Thread.sleep(1000);
            synchronized (mirror){
                System.out.println(name+"1秒后拿到口红锁");
            }

        }
    }

    }

lock锁

◆从JDK 5.0开始,Java提供了更强大的线程同步机制一通过 显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
◆java.util.concurrent.locks.Lock接口是控制多 个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对L ock对象加锁,线程开始访问共享资源之前应先获得L .ock对象
◆ReentrantLock 类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

package XC;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();

        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}
class TestLock2 implements Runnable{
    int ticketnum = 10;
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {

        while (true) {

            try {
                lock.lock(); //加锁
                if (ticketnum>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketnum--);
                }else {
                    break;
                }
            }finally {
                lock.unlock();
            }

        }
    }
}

生产者消费者案例

package XC;

public class TestPC{

    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        new Productor(container).start();
        new Consunmer(container).start();
    }


}


//生产者
class Productor extends Thread{
    SynContainer container;

    public Productor(SynContainer container){
        this.container = container;
    }
    //生产


    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了:"+i+"只鸡");
            container.push(new Chicken(i));
        }
    }
}


//消费者
class Consunmer extends Thread{
    SynContainer container;
    public Consunmer(SynContainer container){
        this.container = container;

    }
//消费


    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了:"+container.pop().id+"只鸡");
        }
    }
}


//产品
class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

//缓冲区
class SynContainer{

    //需要一个容器大小
    Chicken [] chickens = new Chicken[10];
    //容器计数器
    int count = 0;
    //生产者放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了,就需要等待消费者消费
        if (count == chickens.length){
            //通知消费者消费,生产者等待
            try {
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        //如果,没有,满,我们需要继续放入产品
        chickens[count] = chicken;
        count++;

        //可以通知消费者消费了
        this.notify();

    }


    //消费者消费产品
    public synchronized Chicken pop(){
        //判断能否消费
        if (count == 0){
            //等待生产者生产,消费者等待
            try {
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        //如果可有消费
        count--;
        Chicken chicken = chickens[count];
        //吃完了,通知生产者生产
        this.notify();
        return chicken;
            }

}

线程池

◆背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
◆思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

好处:
◆提高响应速度(减少了创建新线程的时间)
◆降低资源消耗(重复利用线程池中线程,不需要每次都创建)
◆便于线程管理

◆corePoolSize:核心池的大小
◆maximumPoolSize:最大线程数
◆keepAliveTime: 线程没有任务时最多保持多长时间后会终止

◆JDK 5.0起提供了线程池相关API: ExecutorService 和Executors
◆ExecutorService: 真正的线程池接口。常见子类ThreadPoolExecutor
◆void execute(Runnable command) :执行任务/命令,没有返回值,-般用来执
行Runnable
◆ Future submit(Callable task):执行任务,有返回值,一般又来执行
Callable
◆void shutdown() :关闭连接池
◆Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池

package XC;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//测试线程池
public class TestPool {

    public static void main(String[] args) {
        //创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);

        //启动线程池
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //关闭链接
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

总结

◆JDK 5.0起提供了线程池相关API: ExecutorService 和Executors
◆ExecutorService: 真正的线程池接口。常见子类ThreadPoolExecutor
◆void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
◆ Future submit(Callable task):执行任务,有返回值, -般又来执行Callable
◆void shutdown() :关闭连接池
◆Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

package XC;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Last {
    public static void main(String[] args) {
        //继承Thread类的启动
        new MyThread1().start();
        //实现Runnable接口的启动
        new Thread(new MyThread2()).start();

       //实现Callable接口的启动
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());

        new Thread(futureTask).start();
        //Callable带有返回值
        try {
            Integer integer = futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        
    }


}
//1.继承Thread类
class MyThread1 extends Thread{
    @Override
    public void run() {
        System. out .println("MyThread1");
    }
}
//2.实现Runnable接口

class MyThread2 implements Runnable{
    @Override
    public void run() {
        System. out . println("MyThread2");
    }
}
//3.实现Callable接口
class MyThread3 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread3");
        return 100;
    }
}
w Thread(new MyThread2()).start();

       //实现Callable接口的启动
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());

        new Thread(futureTask).start();
        //Callable带有返回值
        try {
            Integer integer = futureTask.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        
    }


}
//1.继承Thread类
class MyThread1 extends Thread{
    @Override
    public void run() {
        System. out .println("MyThread1");
    }
}
//2.实现Runnable接口

class MyThread2 implements Runnable{
    @Override
    public void run() {
        System. out . println("MyThread2");
    }
}
//3.实现Callable接口
class MyThread3 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread3");
        return 100;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值