多线程学习

核心概念

  • 线程就是独立的执行路径

  • Main()称之为主线程,为系统的入口,用于执行整个程序

  • 在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的

  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

  • 线程会带来额外的开销,如cpu调度时间,并发控制开销

  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致


Demo1 简单理解线程

public class Demo1 extends Thread {
    @Override
    public void run() {
        //super.run();
        for (int i = 0; i < 20; i++) {
            System.out.println(" 子线程在跑 "+ i);
        }

    }

    public static void main(String[] args) {
        Demo1 demo1 =new Demo1();
        //demo1.run();//普通的执行run,正常顺序
        demo1.start();//创建子线程,执行run,主线程和子线程的执行,由CPU进行调度,无法人为干预
        //
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程在跑"+i);
        }
    }
}
/*
主线程在跑0
 子线程在跑 0
 子线程在跑 1
 子线程在跑 2
 子线程在跑 3
 子线程在跑 4
主线程在跑1
主线程在跑2
主线程在跑3
主线程在跑4
主线程在跑5
主线程在跑6
主线程在跑7
主线程在跑8
主线程在跑9
主线程在跑10
主线程在跑11
主线程在跑12
主线程在跑13
主线程在跑14
主线程在跑15
主线程在跑16
主线程在跑17
 子线程在跑 5
 子线程在跑 6
主线程在跑18
主线程在跑19
 子线程在跑 7
 子线程在跑 8
 子线程在跑 9
 子线程在跑 10
 子线程在跑 11
 子线程在跑 12
 子线程在跑 13
 子线程在跑 14
 子线程在跑 15
 子线程在跑 16
 子线程在跑 17
 子线程在跑 18
 子线程在跑 19
主线程在跑20
主线程在跑21
*/

Demo2 下载网图

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class Demo2 {
    int k=0;
    public static void main(String[] args) {
        WebDownloader wd =new WebDownloader("https://i0.hdslb.com/bfs/album/92033a2dfd1630bf3d013965e1769258ee88eb35.png","1.png");
        WebDownloader wd2 =new WebDownloader("https://i0.hdslb.com/bfs/album/cebd6f76c2317c472e6a0a5610fea0882931cbc3.jpg","2.jpg");
        wd.start();
        wd2.start();
        /*
        下载了:1.png
        下载了:2.jpg
         */
    }
}
class WebDownloader extends Thread{
    private String Url;
    private String ServerFileName;
    public WebDownloader(String url,String serverFileName)
    {
        this.Url = url;
        this.ServerFileName=serverFileName;
    }

    @Override
    public void run() {
        //super.run();
        try {
            //copyURLToFile 方法抛出了异常,上层函数一定要处理这个异常
            FileUtils.copyURLToFile(new URL(this.Url), new File(this.ServerFileName));
            System.out.println("下载了:"+this.ServerFileName);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载图片报错");
        }
    }
}

Demo3 使用Runable接口实现多线程

public class Demo3 implements Runnable {

    @Override
    public void run() {
        //super.run();
        for (int i = 0; i < 20; i++) {
            System.out.println(" 子线程在跑 "+ i);
        }
    }

    public static void main(String[] args) {
        Demo3 demo3 =new Demo3();
        new Thread(demo3).start();//调用方式稍作改变
        //
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程在跑"+i);
        }
    }
}

Demo4 使用Callable的接口实现多线程

public class Demo5  {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        WebDownloader2 wd =new WebDownloader2("https://i0.hdslb.com/bfs/album/92033a2dfd1630bf3d013965e1769258ee88eb35.png","1.png");
        WebDownloader2 wd2 =new WebDownloader2("https://i0.hdslb.com/bfs/album/cebd6f76c2317c472e6a0a5610fea0882931cbc3.jpg","2.jpg");
        ExecutorService service = Executors.newFixedThreadPool(2);
        Future<Boolean> res1 = service.submit(wd);
        Future<Boolean> res2 = service.submit(wd2);
        Boolean r1 = res1.get();
        Boolean r2 = res2.get();
        System.out.println(r1);
        System.out.println(r2);
    }
}
class WebDownloader2 implements Callable<Boolean> {
    private String Url;
    private String ServerFileName;
    public WebDownloader2(String url,String serverFileName)
    {
        this.Url = url;
        this.ServerFileName=serverFileName;
    }
    @Override
    public Boolean call() {
        //super.run();
        try {
            //copyURLToFile 方法抛出了异常,上层函数一定要处理这个异常
            FileUtils.copyURLToFile(new URL(this.Url), new File(this.ServerFileName));
            System.out.println("下载了:"+this.ServerFileName);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载图片报错");
        }
        return true;
    }

}

静态代理模式

Thread 实现对象执行方法的模式

//总结:
//真是执行对象和代理对象都要集成同一个接口
//代理对象要代理真实角色
//好处
//1.代理对象可以做很多真实对象做不了的事,或者说不好做的事
//2.真实对象专注自己的业务
public class Demo6 {
    public static void main(String[] args) {
        Person person = new Person();
        //静态代理类传入 被代理对象
        MarryCompany marryCompany = new MarryCompany(person);
        marryCompany.HappyMarry();//静态代理类,帮助被代理对象执行
        //Tread 类实现新线程执行对象方法就是静态代理模式
        new Thread(()-> System.out.println("Tread 类实现新线程执行对象方法就是静态代理模式")).start();
    }
}

/**
 * Marry接口
 */
interface Marry
{
    void HappyMarry();
}

/**
 * Person类,静态代理实际执行的对象类
 */
class Person implements Marry
{
    @Override
    public void HappyMarry() {
        System.out.println("结婚啦");
    }
}
/**
* MarryCompany Marry代理类
 */
class MarryCompany implements Marry
{
    private Marry _marryPerson;
    public MarryCompany(Marry marryPerson)
    {
        this._marryPerson =marryPerson;
    }
    @Override
    public void HappyMarry() {
        System.out.println("收定金,布置会场");
        this._marryPerson.HappyMarry();
        System.out.println("收尾款");
    }
}

线程状态

五种状态

创建状态 就绪状态 阻塞状态 运行状态 死亡状态

image-20201119154418198

Sleep

Sleep阻塞线程,模拟网络延时可以放大问题的放生性。

public class Demo7 {
    public static void main(String[] args) throws InterruptedException {
        Date currentTime = new Date(System.currentTimeMillis());
        for (int i = 0; i < 10; i++) {
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(currentTime));//java操作时间和c#写起来感觉还是有很大区别的
            Thread.sleep(1000);
            currentTime = new Date(System.currentTimeMillis());
        }
    }
}

线程礼让 yeild

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

public class Demo8 {
    public static void main(String[] args) {
        TestYeild testYeild1 = new TestYeild();
        TestYeild testYeild2 = new TestYeild();
        new Thread(testYeild1,"a").start();
        new Thread(testYeild2,"b").start();
    }
}
class TestYeild implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始运行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"结束运行");
    }
}

Join

Join合并线程,待此线程执行完成之后,在执行其他线程

public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        for (int i = 0; i < 100; i++) {
            if(i==0)
            {
                thread.start();
            }
            if(i==10)
            {
                System.out.println("开始插队=================");
                thread.join();
            }
            System.out.println("主线程"+i);
        }
    }
}
class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("插队线程"+i);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("插队还不快点,还睡觉==============");
    }
}
/*
主线程0
主线程1
主线程2
主线程3
主线程4
主线程5
主线程6
主线程7
主线程8
插队线程0
主线程9
开始插队=================
插队线程1
插队线程2
插队线程3
插队线程4
插队线程5
插队线程6
....
插队线程43
插队线程44
插队线程45
插队线程46
插队线程47
插队线程48
插队线程49
插队还不快点,还睡觉==============
主线程10
主线程11
主线程12
....
主线程88
主线程89
主线程90
主线程91
主线程92
主线程93
主线程94
主线程95
主线程96
主线程97
主线程98
主线程99

Process finished with exit code 0

*/

线程优先级

线程优先级高不一定先执行,但权重高!

min=1 max=10 normal=5

先设置优先级再启动

守护线程

线程分为用户线程和守护线程。虚拟机必须确保用户线程执行完毕。虚拟机不用等待守护线程执行完毕。如后台记录操作日志,监控内存,垃圾回收等待。

public class Demo10 {
    public static void main(String[] args) {
        DeamonThread deamon = new DeamonThread();
        UserThread user = new UserThread();
        Thread deamonThread = new Thread(deamon);
        deamonThread.setDaemon(true);//守护线程默认为false
        deamonThread.start();
        Thread userThread = new Thread(user);
        userThread.start();
    }
}
class UserThread implements Runnable
{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("用户线程执行第"+i);
        }
    }
}
class DeamonThread implements Runnable
{
    @Override
    public void run() {
        //当主线程和其他用户线程执行完毕之后,守护线程也随之关闭
        while(true){
            System.out.println("守护线程执行中");
        }
    }
}

同步方法和同步快

public class Demo4 implements Runnable {
    int ticket = 10;
    @Override
    public synchronized void run() {//锁住run方法
        while (ticket>0)
        {
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
        }
    }

    public static void main(String[] args) {
        Demo4 demo4 = new Demo4();
        new Thread(demo4,"小明").start();
        new Thread(demo4,"小红").start();
        new Thread(demo4,"小蓝").start();
    }
}
import java.util.ArrayList;
import java.util.List;

public class Demo11 {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();//这里实例化跟C#不太一样
        for (int i = 0; i < 20000; i++) {
            new Thread(()->{
                synchronized (list)
                {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        Thread.sleep(6000);
        System.out.println(list.size());//不加 synchronized的情况下 19997!=20000,add当前对象的相同位置,覆盖了
    }
}
public class Demo12 {
    public static void main(String[] args) {
        Account account = new Account("共同账户",100);
        Withdraw withdraw = new Withdraw(account,100);
        new Thread(withdraw).start();
        new Thread(withdraw).start();
        new Thread(withdraw).start();
        new Thread(withdraw).start();
    }
}

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

class Withdraw extends Thread
{
    Account account;
    int withdraw;
    int nowMoney;
    public Withdraw(Account account, int withdraw) {
        this.account = account;
        this.withdraw = withdraw;
    }

    @Override
    public void run()
    {
        try {
            sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (account)
        {
            if(account.money>0 && account.money>=withdraw)
            {
                int rs = this.account.money-this.withdraw;
                this.account.money -=this.withdraw;
                this.nowMoney += this.withdraw;
                System.out.println("余额"+rs+";现在手中的钱有:"+nowMoney);
            }
            else
            {
                System.out.println("余额不足");
            }
        }
    }
}
import java.util.concurrent.CopyOnWriteArrayList;

public class Demo13 {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();//这里实例化跟C#不太一样
        for (int i = 0; i < 20000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(6000);
        System.out.println(list.size());//CopyOnWriteArrayList 线程安全类
    }
}

生产者消费者问题

解决方案一:

并发写作模型“生产者/消费者模式”-->管程法

生产者:负责生产数据的模块

消费者:负责处理数据的模块

缓冲区:消费者不能直接使用生产者的数据,消费者从缓冲区拿出数据

解决方案二:

并发协作模型“生产者/消费者模式”-->信号灯法

通过标志位,判断是否可以通行

public class Demo14 {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        Cook cook =new Cook(synContainer);
        Custom custom = new Custom(synContainer);
        new Thread(cook,"cook").start();
        new Thread(custom,"Custom").start();

    }
}
//产品 鸡
class Chicken
{
    public int id;

    public Chicken(int id) {
        this.id = id;
    }
}
//生产者
class Cook implements Runnable
{
    SynContainer synContainer;
    public Cook(SynContainer synContainer)
    {
        this.synContainer = synContainer;
    }

    @Override
    public void run() {
        // 生产者生产 产品
        for (int i = 0; i < 100; i++) {
            Chicken chicken = new Chicken(i);
            this.synContainer.push(chicken);
            System.out.println("生产者生产了第"+i+"只鸡");
        }
    }

}
//消费者 顾客
class Custom implements Runnable
{
    SynContainer synContainer;
    public Custom(SynContainer synContainer)
    {
        this.synContainer = synContainer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Chicken chicken = this.synContainer.pop();
            System.out.println("消费者消费了第"+chicken.id+"只鸡");
        }
    }
}

class SynContainer
{
    Chicken[] chickens = new Chicken[10];
    int count = 0;

    public synchronized void push(Chicken chicken)
    {
        //容器满了,等待生产者消费
        if(chickens.length == count)
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.chickens[count]=chicken;
        count++;
        //通知消费者,可以消费了
        this.notifyAll();
    }

    public synchronized Chicken pop() {
        if (count == 0) {
            //等待生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Chicken chicken = this.chickens[count-1];
        //this.chickens[count] = null;
        count--;
        this.notifyAll();
        return chicken;
    }
}

notify和notifyAll的差别:notify只唤醒一个线程,且不保证能那个线程会被唤醒,这取决于线程调度器。notifyAll会让等待该锁的所有线程都会被唤醒。

线程池

思路:提前创建好多个线程池,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

好处

提高相应速度(减少了创建新线程的时间)

降低消耗资源(重复利用线程池中线程,不需要每次创建)

便于管理(核心池的大小(放多少线程)、最大线程数(同时跑多少线程)、线程没有任务时最多保持多长时间)

原文地址:https://www.cnblogs.com/huacha/p/14052153.html