多线程基础

一个进程Process可以有多个线程Thread。

进程是执行程序的一次执行过程,是一个动态概念,是系统资源分配的单位。

线程就是独立的执行路径。程序运行时,即使没有自己创建线程,后台也存在线程,如主线程、GC线程。

1.线程的三种创建方式

线程的三种创建方式:继承Thread类、实现Runnable接口、实现Callable接口。

1.继承Thread类

public class TestThread1 extends Thread{
    @Override
    public void run() {
        //run方法线程
        for (int i = 0; i < 21; i++) {
            System.out.println(i);
        }
    }
    //主线程
    public static void main(String[] args) {
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();//调用start方法开启线程
        for (int i = 0; i < 21; i++) {
            System.out.println("main"+i);
        }
    }
}

结果:交替进行

package com.lesson;
​
import org.apache.commons.io.FileUtils;
​
import java.io.File;
import java.io.IOException;
import java.net.URL;
​
//多线程同步下载图片
public class TestThread2 extends Thread{
   private String url;
   private String  name;
   public TestThread2(String url,String name){
       this.url=url;
       this.name=name;
   }
   //下载图片线程的执行体
   @Override
   public void run() {
        WebDownLoader webDownLoader=new WebDownLoader();
        webDownLoader.downLoader(url,name);
        System.out.println(name);
   }
​
    public static void main(String[] args) {
        TestThread2 t1=new TestThread2("https://i1.hdslb.com/bfs/archive/28f5df1e2f2aa0c5b49ad67e34fd312f4bb7e75a.jpg@412w_232h_1c.jpg","1.jpg");
        TestThread2 t2=new TestThread2("https://i1.hdslb.com/bfs/archive/92c9d97eb3d9914d97149bb3bf2c2dea3682855c.jpg@412w_232h_1c.jpg","2.jpg");
        TestThread2 t3=new TestThread2("https://i0.hdslb.com/bfs/archive/4a00d3fb294cb63fc414f6819e277f6548c4134a.jpg@412w_232h_1c.jpg","3.jpg");
​
        t1.start();
        t2.start();
        t3.start();
   }
}
//下载器
class WebDownLoader{
    public void downLoader(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        }catch (IOException e){
            e.printStackTrace();
            System.out.println("IO异常");
        }
    }
}

结果:同时下载

2.实现Runnable接口(推荐使用)

package com.lesson;
//Runnable接口
public class TestThread3 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 21; i++) {
            System.out.println(i);
        }
    }
    //主线程
    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        TestThread3 testThread3 = new TestThread3();
        //创建线程对象,通过线程对象来开启我们的线程
        Thread thread=new Thread(testThread3);
        thread.start();
        for (int i = 0; i < 21; i++) {
            System.out.println("main"+i);
        }
    }
}
​避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
package com.lesson;
//多个线程同时操作同一个对象,如多人买火车票
public class TestThread4 implements Runnable{
   private int ticketNum = 10;
    @Override
    public void run() {
        while (true){
            //Thread.currentThread().getName()获取当前线程的名字
            if (ticketNum<=0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(200);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买走了第"+ticketNum--+"票");
        }
    }
​
    public static void main(String[] args) {
        TestThread4 ticket = new TestThread4();
        new Thread(ticket,"one").start();
        new Thread(ticket,"two").start();
        new Thread(ticket,"three").start();
    }
}
​

结果:

出现多人买走同一张票的情况,线程不安全,数据紊乱,即并发问题。

龟兔赛跑:

package com.lesson;
//龟兔赛跑:同时开始比赛,兔子比龟快,但是兔子开始睡觉,乌龟赢得比赛
public class Race implements Runnable{
    private static String winner;//static保证只有一个winner
    @Override
    public void run() {
        for (int i=1;i<100;i++){
            if (Thread.currentThread().getName().equals("兔子")&&i==10){
                //跑一半时兔子休息
                try {
                    Thread.sleep(10);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            boolean flag=raceOver(i);
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }
    //判断是否完成比赛
    private boolean raceOver(int step){
        if (winner!=null){//已存在胜利者
            return true;
        }else{
            if (step>=20){//判断胜者
                winner=Thread.currentThread().getName();
                System.out.println("赢者:"+winner);
                return true;
            }
        }
        return false;
    }
​
    public static void main(String[] args) {
        Race race=new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

3.实现Callable接口

优点:可以返回值

缺点:实现方式复杂

package com.lesson;
​
import org.apache.commons.io.FileUtils;
​
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
​
public class TestCallable implements Callable<Boolean> {
    private String url;
    private String  name;
    public TestCallable(String url,String name){
        this.url=url;
        this.name=name;
    }
    //下载图片线程的执行体
    @Override
    public Boolean call() {
        WebDownLoader2 webDownLoader=new WebDownLoader2();
        webDownLoader.downLoader(url,name);
        System.out.println(name);
        return true;
    }
​
    public static void main(String[] args) throws ExecutionException,InterruptedException {
        TestCallable t1=new TestCallable("https://i1.hdslb.com/bfs/archive/28f5df1e2f2aa0c5b49ad67e34fd312f4bb7e75a.jpg@412w_232h_1c.jpg","1.jpg");
        TestCallable t2=new TestCallable("https://i1.hdslb.com/bfs/archive/92c9d97eb3d9914d97149bb3bf2c2dea3682855c.jpg@412w_232h_1c.jpg","2.jpg");
        TestCallable t3=new TestCallable("https://i0.hdslb.com/bfs/archive/4a00d3fb294cb63fc414f6819e277f6548c4134a.jpg@412w_232h_1c.jpg","3.jpg");
​
        //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1=ser.submit(t1);
        Future<Boolean> r2=ser.submit(t2);
        Future<Boolean> r3=ser.submit(t3);
        //获取结果
        boolean rs1=r1.get();
        boolean rs2=r2.get();
        boolean rs3=r3.get();
        //关闭服务
        ser.shutdown();
    }
}
class WebDownLoader2{
    public void downLoader(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        }catch (IOException e){
            e.printStackTrace();
            System.out.println("IO异常");
        }
    }
}
​

2. Lambda 表达式

new Thread (()->System.out.println("a")).start();

Lambda表达式:避免匿名内部类定义过多,使代码整洁。

函数式接口(Functional Interface):任何接口,如果只包含唯一一个抽象方法,它就是一个函数式接口。

package com.lesson;
//推导lambda表达式
public class TestLambda1 {
    public static void main(String[] args) {
        iLike like=new Like();//接口回调
        like.lambda();
    }
}
//1.定义一个函数式接口
interface iLike{
    void lambda();
}
//2.实现类
class Like implements iLike{
    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

使用静态内部类进行优化:

public class TestLambda1 {
    //3.静态内部类
    static class Like2 implements iLike{
        @Override
        public void lambda() {
            System.out.println("i like lambda");
        }
    }
​
    public static void main(String[] args) {
        iLike like=new Like();//接口回调
​
        like=new Like2();
        like.lambda();
    }
}
//1.定义一个函数式接口
interface iLike{
    void lambda();
}

使用局部内部类进行优化:

public class TestLambda1 {
    public static void main(String[] args) {
        iLike like=new Like();//接口回调
​
        //4.局部内部类
        class Like3 implements iLike{
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }
​
        like=new Like3();
        like.lambda();
    }
}
//1.定义一个函数式接口
interface iLike{
    void lambda();
}

使用匿名内部类进行优化:

public class TestLambda1 {
    public static void main(String[] args) {
        iLike like=new Like();//接口回调
​
        //5.匿名内部类
        like=new iLike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        like.lambda();
    }
}
//1.定义一个函数式接口
interface iLike{
    void lambda();
}

使用Lambda表达式:

public class TestLambda1 {
    public static void main(String[] args) {
        iLike like=new Like();//接口回调
       
        //6.lambda表达式
        like=()->{
            System.out.println("i like lambda5");
        };
        like.lambda();
    }
}
//1.定义一个函数式接口
interface iLike{
    void lambda();
}

带参数的Lambda表达式:

package com.lesson;
​
public class TestLambda2 {
    public static void main(String[] args) {
        /*完整版
        iLove love=(String a)->{
            System.out.println("i love "+a);
        };*/
        //只有一行代码时,小括号与花括号都可以省去
        //多个参数也可以去掉参数类型,但必须同时删去
        iLove love=(a)->{
            System.out.println("i love "+a);
        };
        love.love("myself");
    }
}
//接口
interface iLove{
    void love(String  a);
}

3.静态代理模式

真实对象和代理对象都需要调用同一个接口,代理对象需要代理真实角色。

package com.lesson;
//静态代理
public class StaticProxy {
    public static void main(String[] args) {
        new Company(new marriedPerson()).HappyMarry();
        //new Thread(()-> System.out.println("Lambda")).start();
        //Thread代理Runnable接口
    }
}
interface Marry{
    void HappyMarry();
}
//真实角色(结婚者)
class marriedPerson implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("恭喜结婚");
    }
}
//代理角色(婚庆公司)
class Company implements Marry{
    //真实目标角色
    private Marry target;
​
    public Company(Marry target) {
        this.target = target;
    }
​
    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();//真实角色
        after();
    }
​
    private void before(){
        System.out.println("布置婚礼");
    }
    private void after(){
        System.out.println("收拾结束");
    }
}

优点:代理对象可以实现比真实对象更多的方法,真实对象只需专注自己的事情。

4.线程状态

1.停止线程

package com.lesson;
//建议线程正常停止->利用标志位。
//不要使用stop()或destroy()等过时或JDK不建议使用的方法
public class TestStop implements Runnable{
   private boolean flag=true;//标志位
    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("run"+i++);
        }
    }
    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 < 1001; i++) {
            System.out.println("main"+i);
            if (i==900){
                testStop.stop();//切换标志位,使线程停止
                System.out.println("stop");
            }
        }
    }
}

结果:

主线程跑完,线程停止。

2.线程休眠

sleep

package com.lesson;
//模拟倒计时
public class TestSleep {
    public static void main(String[] args) {
        try {
            tenDown();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    public static void tenDown()throws InterruptedException{
        int num=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num<=0){
                break;
            }
        }
    }
}

3.线程礼让

yield

让当前正在执行的线程暂停(即转为就绪状态),但不阻塞。

让CPU重新调度,礼让不一定成功

package com.lesson;
​
public class TestYield {
    public static void main(String[] args) {
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"线程开始执行");
            Thread.yield();//礼让
            System.out.println(Thread.currentThread().getName()+"线程结束执行");
        },"a").start();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"线程开始执行");
            System.out.println(Thread.currentThread().getName()+"线程结束执行");
        },"b").start();
    }
}

结果:

4.线程强制执行

Join合并线程,其他线程处于阻塞状态,待此线程执行完成后,再执行其他线程。

package com.lesson;
//类似插队
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("vip"+i);
        }
    }
​
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin=new TestJoin();
        Thread thread=new Thread(testJoin);
        thread.start();
        for (int i = 0; i < 1000; i++) {
            if (i==200){
                thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

5.线程状态观测

Thread.State:

  • NEW 尚未启动

  • RUNNABLE 在Java虚拟机中执行

  • BLOCKED 被阻塞等待监视器锁定

  • WAITING 正在等待另一个线程执行特定动作

  • TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间

  • TERMINATED 已退出

package com.lesson;
//线程状态观测
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            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);
        //观察启动后
        thread.start();
        state=thread.getState();
        System.out.println(state);
​
        while (state!=Thread.State.TERMINATED){
            //线程未终止
            Thread.sleep(100);
            state=thread.getState();
            System.out.println(state);
        }
    }
}

结果:

线程只能启动一次。

5.线程优先级

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

优先级范围1-10:

  • Thread.MIN_PRIORITY=1;

  • Thread.MAX_PRIORITY=10;

  • Thread.NORM_PRIORITY=5;

更改方式:getPriority().setPriority(int x)

package com.lesson;
//测试线程优先级
public class TestPriority extends Thread{
    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();//默认5
​
        t2.setPriority(1);
        t2.start();
​
        t3.setPriority(4);
        t3.start();
​
        t4.setPriority(Thread.MAX_PRIORITY);//10
        t4.start();
​
        t5.setPriority(3);
        t5.start();
    }
}
class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

结果:

优先级只是意味着获得调度的概率低,不代表不被调用,最终还是看CPU的调度。

6.守护线程

线程分为用户线程和守护(daemon)线程。

虚拟机必须确保用户线程执行完毕,如main,不用等待守护线程执行完毕,如gc垃圾回收。

package com.lesson;
​
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        Person person = new Person();
​
        Thread thread=new Thread(god);
        thread.setDaemon(true);//默认false表示用户线程
        thread.start();
​
        new Thread(person).start();
    }
}
class Person implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("live");
        }
        System.out.println("=========goodbye========");
    }
}
class God implements Runnable{
    @Override
    public void run() {
        System.out.println("God");
    }
}

7.线程同步

并发问题:多个线程操作同一个对象。

线程同步:等待机制->多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。sleep不会释放锁。

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

同步块:synchronized(obj){

}

锁的对象必须是变化的量。

package com.lesson;
​
import java.util.concurrent.CopyOnWriteArrayList;
​
//测试JUC安全类型的集合
public class TestJUC {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

8.死锁

产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用。

  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

  3. 不剥夺条件:进程已获得的资源在未使用完之前,不能强行剥夺。

  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

9.Lock

ReentrantLock类(可重入锁)实现了Lock,它拥有与synchronized相同的并发性和内存语义。可以手动开启和关闭锁。

package com.lesson;
​
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 ticket =10;
    //定义Lock锁
    private final ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();//加锁
            try {
                if (ticket>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+ticket--);
                }else {
                    break;
                }
            }finally {
                //解锁
                lock.unlock();
            }
        }
    }
}

10.线程协作

生产者消费者问题。->管程法、信号灯法。

1.管程法

package com.lesson;
//生产者消费者问题->利用缓冲区解决:管程法
public class TestPC {
    public static void main(String[] args) {
        SynContainer container=new SynContainer();
        new Producer(container).start();
        new Consumer(container).start();
    }
}
//生产者
class Producer extends Thread{
    SynContainer container;
    public Producer(SynContainer container){
        this.container=container;
    }
​
    //生产
    @Override
    public void run() {
        for (int i = 1; i < 100; i++) {
            try {
                container.put(new Production(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//消费者
class Consumer extends Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container=container;
    }
​
    //消费
    @Override
    public void run() {
        for (int i = 1; i < 100; i++) {
            try {
                container.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//产品
class Production {
    int id;//产品编号
    public Production(int id) {
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    Production[] productions=new Production[10];
    int count=0;
    //生产者放入产品
    public synchronized void put(Production production) throws InterruptedException {
        //当容器满时,等待 使用while可以解决多个生产者或消费者的问题
        while (count==productions.length){
            //通知消费者取出产品
            this.wait();
        }
        //如果容器未满,放入产品
        productions[count]=production;
        count++;
        //将输出语句放入put()方法,以免线程切换了,但仍打印出生产了第11个产品
        System.out.println("生产了第"+count+"个产品");
        //放入产品后,通知消费者取出产品
        this.notifyAll();
    }
    //消费者取出产品
    public synchronized Production get() throws InterruptedException {
        //当容器空时,等待
        while (count==0){
            //通知生产者放入产品
            this.wait();
        }
        //如果容器不为空,取出产品
        count--;
        Production production=productions[count];
        System.out.println("消费了第"+(count+1)+"个产品");
        //取出产品后,通知生产者放入产品
        this.notifyAll();
        return production;
    }
}

2.信号灯法

package com.lesson;
//生产者消费者问题->信号灯法
public class TestPC2 {
    public static void main(String[] args) {
        TV tv=new TV();
        new Actor(tv).start();
        new Audience(tv).start();
    }
}
//生产者->演员
class Actor extends Thread{
    TV tv;
    public Actor(TV tv){
        this.tv=tv;
    }
​
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                try {
                    this.tv.act("节目A");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                try {
                    this.tv.act("节目B");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//消费者->观众
class  Audience extends Thread{
    TV tv;
    public Audience(TV tv){
        this.tv=tv;
    }
​
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                tv.view();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//产品->节目
class TV{
    //true 演员录制节目,观众等待;false 观众观看节目,演员等待
    String program;//表演的节目
    boolean flag=true;
​
    //录制节目
    public synchronized void act(String program) throws InterruptedException {
        while (!flag){
            this.wait();
        }
        System.out.println("演员录制"+program);
        //录制完成,通知观众观看
        this.notifyAll();
        this.program=program;
        this.flag=!this.flag;
    }
    //观看节目
    public synchronized void view() throws InterruptedException {
        while (flag){
            this.wait();
        }
        System.out.println("观众观看"+program);
        //观看完毕,通知演员录制
        this.notifyAll();
        this.flag=!this.flag;
    }
}

11.线程池

经常创建、销毁、使用量特别大的资源,如并发情况下的线程,对性能影响很大。

提前创建好 多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。

优点:提高响应速度,降低资源消耗,便于线程管理。

Callable接口实现线程池见上,可以有返回值。

Runnable接口实现线程池(没有返回值):

package com.lesson;
​
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
​
//Runnable接口创建线程池
public class TestPool {
    public static void main(String[] args) {
        //1.创建线程池
        ExecutorService service= Executors.newFixedThreadPool(10);//线程池大小
        service.execute(new MyThread());
        service.execute(new MyThread());
        //2.关闭服务
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值