多线程学习

  • Post author:
  • Post category:其他




线程的三种创建方式


1:继承Thread类

//创建线程的快捷方式一:继承Thread,重写run方法,调用start开启线程;
//总结:注意线程开启不一定执行,由cpu调度执行
public class TestThread1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码=="+i);
        }
    }

    public static void main(String[] args) {
        TestThread1 testThread1=new TestThread1();
        testThread1.start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("我在学习多线程=="+i);
        }
    }
}
//练习Thread,实现多线程下载图片
public class TestThread2 extends Thread{
    private  String name;
    private  String url;
    public TestThread2(String name, String url) {
        this.name = name;
        this.url = url;
    }
    @Override
   public void run() {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(name,url);
        System.out.println("下载文件名为"+name);
    }


    public static void main(String[] args) {
        TestThread2 testThread1=new TestThread2("1.jpg","https://img0.baidu.com/it/u=648074082,2528336694&fm=253&fmt=auto&app=138&f=JPEG?w=960&h=491");
        TestThread2 testThread2=new TestThread2("2.jpg","https://img1.baidu.com/it/u=1690337816,4247323366&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=701");
        TestThread2 testThread3=new TestThread2("3.jpg","https://img2.baidu.com/it/u=638721022,3403685495&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500");
        testThread1.start();
        testThread2.start();
        testThread3.start();
    }

}
class WebDownloader{
    public void downloader(String name, String url){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("io异常");
        }
    }
}


不建议使用:避免oop单继承局限性;

2:实现Runnable接口

public class TestThread3 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码=="+i);
        }
    }

    public static void main(String[] args) {
        TestThread3 testThread3=new TestThread3();
        new Thread(testThread3).start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("我在学习多线程=="+i);
        }
    }
}


推荐使用:避免单继承局限性,零活方便,方便同一个对象被多个线程使用;

抢票案例:

public class TestThread4 implements Runnable {
    private int ticketNums=10;
    @Override
    public void run() {
        while (true){
            if (ticketNums<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
        }
    }

    public static void main(String[] args) {
        TestThread4 testThread4=new TestThread4();
        new Thread(testThread4,"小王").start();
        new Thread(testThread4,"小张").start();
        new Thread(testThread4,"小志").start();
    }
}

龟兔赛跑案例:

public class Race implements Runnable {
    private static String winner;
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            boolean b = gameOver(i);
            if (b==true){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"已经跑了"+i+"步");
        }
    }
    private boolean gameOver(int steps){
        if (winner!=null){
            return true;
        }
        if (steps>=100){
            winner=Thread.currentThread().getName();
            System.out.println("WINNER IS "+winner);
            return true;
        }else {
            return false;
        }
    }

    public static void main(String[] args) {
        Race race=new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

3:Callable接口

public class TestCallable implements Callable {
    String url;
    String name;

    public TestCallable(String name, String url) {
        this.url = url;
        this.name = name;
    }

    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(name,url);
        System.out.println("下载文件名为"+name);
        return true;
    }
    class WebDownloader {
        public void downloader(String name, String url) {
            try {
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("io异常");
            }
        }
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable testThread1=new TestCallable("1.jpg","https://img0.baidu.com/it/u=648074082,2528336694&fm=253&fmt=auto&app=138&f=JPEG?w=960&h=491");
        TestCallable testThread2=new TestCallable("2.jpg","https://img1.baidu.com/it/u=1690337816,4247323366&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=701");
        TestCallable testThread3=new TestCallable("3.jpg","https://img2.baidu.com/it/u=638721022,3403685495&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500");

        //创建服务
        ExecutorService executorService= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> result1=executorService.submit(testThread1);
        Future<Boolean> result2=executorService.submit(testThread2);
        Future<Boolean> result3=executorService.submit(testThread3);
        //获取结果
        Boolean aBoolean = result1.get();
        Boolean aBoolean2 = result2.get();
        Boolean aBoolean3 = result3.get();
        //关闭服务
        executorService.shutdownNow();
    }

}



静态代理

public class StacticProxy {
    public static void main(String[] args) {
        ZhongJie zhongJie=new ZhongJie(new Mine());
        zhongJie.Sleep();
    }
}

interface ZuFang {
    void Sleep();
}

class Mine implements ZuFang {
    @Override
    public void Sleep() {
        System.out.println("租房是为了晚上有睡觉的地方");
    }
}
class ZhongJie implements ZuFang{
    private  Mine mine;

    ZhongJie(Mine mine) {
        this.mine = mine;
    }

    @Override
    public void Sleep() {
        before();
        mine.Sleep();
        after();
    }
    private void before(){
        System.out.println("中介找房源");
    }
    private void after(){
        System.out.println("事成签合同");
    }
}

总结:真实对象和代理对象必须实现同一个接口;

代理对象代理真实对象;



Lamda表达式

理解函数式接口,是学习lamda表达式的关键所在;

函数式接口的定义:

1  任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口;

public interface Runnable {
    public abstract void run();
}

2  对于函数式接口,我们可以通过lambda来创建该接口的对象;

详列:


public class TestLamdba1 {
    //3 静态内部类
    static class Like2 implements ILike {
        @Override
        public void lamdba() {
            System.out.println("I like lambda 2");
        }
    }

    public static void main(String[] args) {
        //4 局部内部类
        class Like3 implements ILike{
            @Override
            public void lamdba() {
                System.out.println("i like lambda3");
            }
        }
        //5 匿名内部类
        ILike like4 = new ILike() {
            @Override
            public void lamdba() {
                System.out.println("i like lambda4");
            }
        };
        like4.lamdba();
        ILike like = new Like();
        like.lamdba();
        ILike like2 = new Like2();
        like2.lamdba();
        ILike like3 = new Like3();
        like3.lamdba();
        //6 用lambda简化
        ILike like6=new Like();
        like6=()->{
            System.out.println("i like lambda6");
        };
        like6.lamdba();
        //7 简化lambda表达式
        like6=()-> System.out.println("i like lambda7");
        like6.lamdba();
        //8 总结
            /*lambda表达式在只有一行的情况下才能省略{};
            前提必须是函数式接口;
            多个参数也可以去掉(a ,b)->{};*/
    }
}

//1 定义一个函数式接口
interface ILike {
    void lamdba();
}

//2 实现类
class Like implements ILike {
    @Override
    public void lamdba() {
        System.out.println("i like lambda");
    }
}



线程的五大状态

创建:Thread  t = new Thread;线程对象一旦创建就进入新生状态;

就绪:当调用start方法,线程就会立即进入就绪状态,但不意味着调度执行;

运行状态:进入运行状态,线程才真正执行线程体的代码块;

阻塞:当调用sleep,wait或同步锁时,线程进入阻塞状态,代码不在往下执行,阻塞事件解除后,重信进入就绪状态,等待cpu调度执行;

死亡:线程中断或者结束,就会进入死亡状态,不能重新启动;

测试停止线程:


//测试stop
//建议线程正常停止--->利用次数不建议死循环;
//建议使用标志位---->设置一个标志位;
//不要使用stop或者destory等过时或者jdk不建议使用的方法;
public class TestStop implements Runnable {
    //设置一个标识位
    private boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("线程正在执行");
        }
    }
    public void stop(){
        this.flag=false;
    }
    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("i的值是--->"+ i);
            if (i==1){
                testStop.stop();
                System.out.println("线程已停止");
            }
        }
    }
}

sleep线程休眠:

1:指定当前线程阻塞的毫秒数;

2:sleep时间到达后线程进入就绪状态;

3:每个对象都有一把锁,sleep不会释放锁;


//模拟倒计时
public class TestSleep implements Runnable {
    @Override
    public void run() {
        int num=10;
        while (true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(num--);
            if (num<=0){
                break;
            }
        }
    }

    public static void main(String[] args) {
        TestSleep testSleep=new TestSleep();
        new Thread(testSleep).start();
    }
}

线程礼让:

1:让当前执行的线程暂停,但不阻塞;

2:将线程从就绪状态变为就绪;

3:礼让不一定成功;



public class TestYield {
    public static void main(String[] args) {
        Yield yield=new Yield();
        new Thread(yield,"a").start();
        new Thread(yield,"b").start();
    }
}
class Yield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"结束执行");
    }
}

线程强制执行join:

join合并线程,待此线程执行完毕后,再执行其他线程;


//测试join方法
public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        JoinTest joinTest=new JoinTest();
        Thread thread=new Thread(joinTest);
        thread.start();
        for (int i = 0; i < 100; i++) {
            if (i==20){
                System.out.println("i的值是---->"+i);
                thread.join();
            }
        }
    }
}
class JoinTest implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程vip来了------->");
        }
    }
}



线程状态观测(Thread.State)


NEW:尚未启动的线程处于此状态;


Runnable:在java虚拟机中执行的线程处于此状态;

Blocked:被阻塞等待监视器锁定的线程处于此状态;

WAITTING:正在等待另一个线程执行特定动作的线程处于此状态;

TIMED_WAITTING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态;

TERMINATED:已退出的线程处于此状态;



线程优先级:

Java提供一个线程调度器来监控程序启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行;

线程的优先级用数字来表示,范围1-10;

Thread.MIN_PRIORITY = 1;

Thread.MAX_PRIORITY = 10;

Thread.NORM_PRIORITY = 5;

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



守护线程


线程分为用户线程和守护线程,虚拟机需要确保用户线程执行完毕,虚拟机不需要等待守护线程执行完毕;



//测试守护线程和用户线程
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//设置为守护线程
        thread.start();
        You you = new You();
        new Thread(you).start();//默认是用户线程

    }
}

class God implements Runnable {

    @Override
    public void run() {
        while(true){
            System.out.println("上帝守护着你");
        }
    }
}
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你开心的活着");
        }
        System.out.println("==========goodbye world========");
    }

}



线程同步

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

1:一个线程持有锁会导致其它所有需要锁的线程挂起;

2:在多线程竞争下,加锁,释放锁会导致较多的上下文切换和调度延时,引起性能问题;

3:如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题;



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

//线程不安全集合
public class UnSafeList {
    public static void main(String[] args) throws InterruptedException {
        List list = new ArrayList();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}



同步方法:

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

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

//线程不安全集合
public class UnSafeList {
    public static void main(String[] args) throws InterruptedException {
        List list = new ArrayList();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

锁的应该是变化的属性;



死锁


多个线程各自占有一些共享资源,并且互相等待其它线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况;




public class DealLockTest {
    public static void main(String[] args) {
        MakeUp makeUp =new MakeUp(0,"小米");
        makeUp.start();
        MakeUp makeUp2 =new MakeUp(1,"华为");
        makeUp2.start();
    }
}
class LipStick{};
class Mirror{};
class MakeUp extends Thread{
    static LipStick lipStick = new LipStick();
    static Mirror mirror = new Mirror();
    int choice;
    String name;
    @Override
    public void run() {
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    MakeUp(int choice,String name){
        this.choice=choice;
        this.name=name;
    }
    private void makeUp() throws InterruptedException {
        if (choice==0){
            synchronized (lipStick){
                System.out.println(Thread.currentThread().getName()+"拿到了口红");
                Thread.sleep(1000);
                synchronized (mirror){//拿镜子锁
                    System.out.println(Thread.currentThread().getName()+"拿到了镜子");
                }
               /* synchronized (mirror){//拿镜子锁
                    System.out.println(Thread.currentThread().getName()+"拿到了镜子");
                }*/
            }
        }else {
            synchronized (mirror){
                System.out.println(Thread.currentThread().getName()+"拿到了镜子");
                Thread.sleep(1000);
                synchronized (lipStick){
                    System.out.println(Thread.currentThread().getName()+"拿到了口红");
                }
            }
           /* synchronized (lipStick){
                System.out.println(Thread.currentThread().getName()+"拿到了口红");
            }*/
        }
    }

}



Lock锁(可重入锁)



import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        Lock lock = new Lock();
        new Thread(lock).start();
        new Thread(lock).start();
        new Thread(lock).start();
    }
}

class Lock implements Runnable {
    int ticketNums = 10;
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (ticketNums > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                } else {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
}



线程协作

wait:表示线程一直等待,直到其它线程通知,与sleep不同,会释放锁;

notify():唤醒一个处于等待状态的线程;

notifyAll():唤醒同一个对象所有调用wait方法的线程,优先级别高的线程优先调度;

管程法示例:


//测试管程法
public class TestPc {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}

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

    public Productor(SynContainer synContainer) {
        this.synContainer = synContainer;
    }


    @Override
    public synchronized void run() {
        for (int i = 1; i < 101; i++) {
            try {
                synContainer.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
};

//消费者
class Consumer extends Thread {
    SynContainer synContainer;

    public Consumer(SynContainer synContainer) {
        this.synContainer = synContainer;
    }

    @Override
    public synchronized void run() {
        for (int i = 1; i < 101; i++) {

            try {
                synContainer.pop();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
};

//产品
class Chicken {
    int id;

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

//缓冲区
class SynContainer {
    Chicken[] chickens = new Chicken[10];
    private int count = 0;//计数器

    //生产者放入产品
    public synchronized void push(Chicken chicken) throws InterruptedException {
        if (count == chickens.length) {
            //货柜已经满了,通知消费者消费
            System.out.println("货柜已满,请消费者消费");
            this.wait();
        } if (true){
            chickens[count] = chicken;
            System.out.println("货柜第" + count + "位置放入了一只鸡,请消费");
            count++;
            this.notifyAll();
        }
    }

    public synchronized Chicken pop() throws InterruptedException {
        if (count == 0) {
            System.out.println("货柜里面没有鸡,消费者正在等待");
            this.wait();
        }
        System.out.println("货柜第" + count + "位置的鸡被消费");
        count--;
        Chicken chicken = chickens[count];
        //通知生产者生产鸡
        this.notifyAll();
        return chicken;


    }
}



线程池

背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大;

思路:可以创建好多个线程,放入线程池当中,使用时直接获取;

corePoolSize:核心线程池的大小;

maxmumPoolSize:最大线程数;

keepAliveTime:线程没有任务时最多保持多长时间后会终止;



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.shutdown();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println("测试线程");
    }
}



版权声明:本文为bugbugkingking原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。