1.1 线程简介

多任务→多线程

过程 VS 线程

  • 程序是指令和数据的有序汇合,是一个动态的概念。
  • 过程是程序的一次执行过程,是一个动静的概念。
  • 过程中至多有一个线程,线程是CPU调度和执行的根本单位。

1.2 线程创立

三种创立形式:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口(理解)

1.2.1 继承Thread类

创立线程形式一:继承Thread类→重写run()办法→调用start()启动线程

//留神,线程开启不肯定立刻执行,由CPU调度执行public class TestThread1 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线程,主线程        TestThread1 testThread1 = new TestThread1();        //调用start()办法开启线程,交替执行        testThread1.start();        for(int i = 0;i < 20;i++) {            System.out.println("我在学习多线程—————"+i);        }    }    }

实现多线程同步下载图片

import java.io.File;import java.net.URL;import org.apache.commons.io.FileUtils;public class TestThread2 extends Thread{    private String url,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://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","1.jpg");        TestThread2 t2 = new TestThread2("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","2.jpg");        TestThread2 t3 = new TestThread2("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","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 (Exception e) {            e.printStackTrace();            System.out.println("IO异样,downloader办法呈现问题");        }    }}

1.2.2 实现Runnable接口

创立线程形式二:实现Runnable接口→重写run()办法→调用start()启动线程(须要Runnable接口实现类)

public class TestThread3 implements Runnable{    @Override    public void run() {        // run办法线程体        for(int i = 0;i < 20;i++) {            System.out.println("我在看代码—————"+i);        }    }    public static void main(String[] args) {        //创立runnable接口的实现类对象        TestThread3 testThread3 = new TestThread3();                //创立线程对象,通过线程对象来开启线程,代理//        Thread thread = new Thread(testThread3);//        thread.start();        new Thread(testThread3).start();                for(int i = 0;i < 20;i++) {            System.out.println("我在学习多线程—————"+i);        }    }}
import java.io.File;import java.net.URL;import org.apache.commons.io.FileUtils;//练习Thread,实现多线程同步下载图片public class TestThread2n implements Runnable{    private String url,name; //网络图片地址,保留的文件名    public TestThread2n(String url,String name) {        this.url = url;        this.name = name;    }    //下载图片线程执行体    @Override    public void run() {        WebDownloader1 webDownloader = new WebDownloader1();        webDownloader.downloader(url, name);        System.out.println("下载了文件名为:"+name);    }    public static void main(String[] args) {        TestThread2n t1 = new TestThread2n("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","1.jpg");        TestThread2n t2 = new TestThread2n("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","2.jpg");        TestThread2n t3 = new TestThread2n("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","3.jpg");                new Thread(t1).start();        new Thread(t2).start();        new Thread(t3).start();    }}//下载器class WebDownloader1{    //下载办法    public void downloader(String url,String name) {        try {            FileUtils.copyURLToFile(new URL(url), new File(name));        }catch (Exception e) {            e.printStackTrace();            System.out.println("IO异样,downloader办法呈现问题");        }    }}

小结:

  • 继承Thread类

    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start()
    • 不倡议应用:防止OOP单继承局限性
  • 实现Runnable接口

    • 实现接口Runnable具备多线程能力
    • 启动线程:传入指标对象+Thread对象.start()
    • 举荐应用:防止单继承局限性,灵便不便,不便同一对象被多个线程应用

多个线程同时操作一个对象,买火车票的例子

//问题:多个线程操作同一个资源,线程不平安,数据错乱public class TestThread4 implements Runnable{    //票数    private int ticketNum = 10;    @Override    public void run() {        while(true) {            if(ticketNum<=0) {                break;            }            try {                Thread.sleep(10);            } catch (Exception e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票");        }        }    public static void main(String[] args) {        TestThread4 ticket = new TestThread4();        new Thread(ticket,"小明").start();        new Thread(ticket,"老师").start();        new Thread(ticket,"黄牛党").start();    }}

案例:龟兔赛跑

//模仿龟兔赛跑public class Race implements Runnable{    //胜利者    private static String winner;     public static void main(String[] args) {        Race race = new Race();        new Thread(race,"兔子").start();        new Thread(race,"乌龟").start();    }    @Override    public void run() {        for(int i = 0; i <= 100;i++) {            //模仿兔子劳动            if("兔子".equals(Thread.currentThread().getName() )&& i%10==5) {                try {                    Thread.sleep(56);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            if("乌龟".equals(Thread.currentThread().getName() )) {                try {                    Thread.sleep(5);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            //判断较量是否完结            boolean flag = gameOver(i);            //如果较量完结了,就进行程序            if(flag) {                break;            }            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");        }        }    //判断是否实现较量    private boolean gameOver(int steps) {        //判断是否有胜利者        if(winner!=null) {            return true;        }else {            if(steps==100) {                winner = Thread.currentThread().getName();                System.out.println("winner is "+winner);                return true;            }        }        return false;    }}

1.2.3 实现Callable接口

创立线程形式三(理解即可):实现Callable接口(须要返回值类型)→重写call()办法(须要抛出异样)→创立指标对象→创立敞开服务

import java.io.File;import java.net.URL;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import org.apache.commons.io.FileUtils;//创立形式三:实现Callable接口/* * Callable的益处: * 1.能够定义返回值 * 2.能够抛出异样 */public class TestCallable implements Callable<Boolean>{    private String url,name; //网络图片地址,保留的文件名    public TestCallable(String url,String name) {        this.url = url;        this.name = name;    }    //下载图片线程执行体    @Override    public Boolean call() {        WebDownloader webDownloader = new WebDownloader();        webDownloader.downloader(url, name);        System.out.println("下载了文件名为:"+name);        return true;    }    public static void main(String[] args) throws Exception {        TestCallable t1 = new TestCallable("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","1.jpg");        TestCallable t2 = new TestCallable("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","2.jpg");        TestCallable t3 = new TestCallable("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","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();                System.out.println(rs1);        System.out.println(rs2);        System.out.println(rs3);        //敞开服务        ser.shutdown();    }} //下载器class WebDownloader{    //下载办法    public void downloader(String url,String name) {        try {            FileUtils.copyURLToFile(new URL(url), new File(name));        }catch (Exception e) {            e.printStackTrace();            System.out.println("IO异样,downloader办法呈现问题");        }    }}

Lamda表达式

语法:(parameters) -> expression 或 (parameters) ->{ statements; }

作用:简化代码,防止匿名外部类定义过多

Function Interface(函数式接口):只蕴含惟一一个形象办法

能够通过lamda表达式来创立函数式接口的对象

推导lambda表达式

public class TestLambda1 {    //3.动态外部类    static class Like2 implements ILike{        @Override        public void lambda() {            System.out.println("I like lambda2");        }            }        public static void main(String[] args) {        ILike like = new Like();        like.lambda();        like = new Like2();        like.lambda();                //4.部分外部类        class Like3 implements ILike{            @Override            public void lambda() {                System.out.println("I like lambda3");            }                }        like = new Like3();        like.lambda();                //5.匿名外部类,没有类的名称,必须借助接口或者父类        like = new ILike() {            @Override            public void lambda() {                System.out.println("I like lambda4");            }        };        like.lambda();                //6.用lambda简化        like = () -> {            System.out.println("I like lambda5");        };        like.lambda();    }}//1.定义一个函数式接口interface ILike{    void lambda();}//2.实现类class Like implements ILike{    @Override    public void lambda() {        System.out.println("I like lambda");    }    }

简化lambda表达式:1.省略参数类型 2.省略括号

动态代理

示例(婚庆公司)

public class StaticProxy {    public static void main(String[] args) {        You you = new You();         you.HappyMarry();        //        new Thread(()->System.out.println("我爱你")).start();//        new WeddingCompany(new You()).HappyMarry();        //代理        WeddingCompany weddingCompany = new WeddingCompany(new You());        weddingCompany.HappyMarry();    }}interface Marry{    void HappyMarry();}//实在角色class You implements Marry{    @Override    public void HappyMarry() {        System.out.println("结婚了,开心");            }}//代理角色class WeddingCompany implements Marry{    private Marry target;        public WeddingCompany(Marry target) {        this.target = target;    }    @Override    public void HappyMarry() {        before();        this.target.HappyMarry(); //实在对象        after();    }    private void after() {        System.out.println("结婚之后,收尾款");            }    private void before() {        System.out.println("结婚之前,安排现场");            }    }

动态代理模式总结:

  • 实在对象和代理对象都要实现同一个接口
  • 代理对象代理实在对象

益处:

  • 代理对象能够做很多实在对象做不了的事件
  • 实在对象专一做本人的事件

1.3 线程状态

1.3.1 五大状态

1.3.2 线程进行

  • 不举荐应用JDK提供的stop()、destroy()办法【已废除】
  • 举荐线程本人停下来(倡议应用一标记位进行终止变量)
//测试stop//1.倡议线程失常进行-->利用次数,不倡议死循环//2.倡议应用标记位//3.不要应用stop或者destroy等过期或者JDK不倡议应用的办法public class TestStop implements Runnable{    private boolean flag = true;    public static void main(String[] args) {        TestStop testStop = new TestStop();        new Thread(testStop).start();        for(int i = 0; i < 1000; i++) {            System.out.println("main"+i);            if(i == 900) {                //调用stop()办法切换标记位,让线程进行                testStop.stop();                System.out.println("线程进行");            }        }    }    @Override    public void run() {        int i = 0;        while(flag) {            System.out.println("run ... Thread"+i++);        }        }    public void stop() {        this.flag = false;    }}

1.3.3 线程休眠

sleep(毫秒)→就绪

每个对象有个锁,sleep不会开释锁

//模仿网络延时:放大问题的产生性public class TestSleep implements Runnable{    //票数    private int ticketNum = 10;    @Override    public void run() {        while(true) {            if(ticketNum<=0) {                break;            }            //模仿延时            try {                Thread.sleep(100);            } catch (Exception e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票");        }        }    public static void main(String[] args) {        TestSleep ticket = new TestSleep();        new Thread(ticket,"小明").start();        new Thread(ticket,"老师").start();        new Thread(ticket,"黄牛党").start();    }}
import java.text.SimpleDateFormat;import java.util.Date;public class TestSleep2{    public static void main(String[] args) {        //模仿倒计时        try {            tenDown();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();                    }        //打印以后零碎工夫        Date startTime = new Date(System.currentTimeMillis());        while(true) {            try {                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));                Thread.sleep(1000);                startTime = new Date(System.currentTimeMillis()); //更新以后工夫            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }            }    public static void tenDown() throws InterruptedException {        int num = 3;        while(true) {            Thread.sleep(1000);            System.out.println(num--);            if(num<=0) {                break;            }        }        }}

1.3.4 线程礼让

Yield让以后线程暂停但不阻塞,转为就绪状态

礼让不肯定胜利

//测试礼让线程,礼让不肯定胜利public class TestYield {    public static void main(String[] args) {        // TODO Auto-generated method stub        MyYield myYield = new MyYield();        new Thread(myYield,"a").start();        new Thread(myYield,"b").start();    }}class MyYield implements Runnable{    @Override    public void run() {        // TODO Auto-generated method stub        System.out.println(Thread.currentThread().getName()+"线程开始执行");        Thread.yield(); //礼让        System.out.println(Thread.currentThread().getName()+"线程进行执行");    }    }

1.3.5 线程强制执行

Join合并线程,待此线程执行实现后,再执行其余线程,其余线程阻塞(能够看作是插队)

//测试join办法public class TestJoin implements Runnable{    public static void main(String[] args) throws InterruptedException {        //启动咱们的线程        TestJoin testJoin = new TestJoin();        Thread thread = new Thread(testJoin);        thread.start();        for (int i = 0; i < 500; i++) {            if(i==200) {                thread.join();//插队            }            System.out.println("main"+i);        }    }    @Override    public void run() {        for (int i = 0; i < 1000; i++) {            System.out.println("线程vip来了"+i);        }        }}

1.3.6 线程优先级

优先高的不肯定先执行,默认是5

//测试线程的优先级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);        //先设置优先级,再启动        t1.start();        t2.setPriority(1);        t2.start();        t3.setPriority(4);        t3.start();        t4.setPriority(Thread.MAX_PRIORITY); //MAX_PRIORITY=10        t4.start();    }}class MyPriority implements Runnable{    @Override    public void run() {    System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());            }    }

1.3.7 守护(daemon)线程

线程分为用户线程守护线程

虚拟机必须确保用户线程(如,后盾记录操作日志,监控内存,垃圾回收期待)执行结束,但不必期待守护线程执行结束

//测试守护线程//上帝守护你public class TestDaemon {    public static void main(String[] args) {        God god = new God();        You you = new You();        Thread thread = new Thread(god);        thread.setDaemon(true); //默认是false示意是用户线程,失常的线程都是用户线程        thread.start(); //守护线程启动        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("====Good bye!====");    }    }

1.4 线程同步

多个线程操作同一资源(并发)

线程同步是一种期待机制,多个须要同时拜访同一对象的线程进图该对象的期待池造成队列,期待后面线程应用结束,下一线程再应用

造成条件:队列+锁(synchronized)

1.4.1 同步办法及同步块

三个不平安案例

1.不平安的买票

//不平安的买票(线程不平安)public class UnsafeBuyTicket {    public static void main(String[] args) {        BuyTicket station = new BuyTicket();        new Thread(station,"苦逼的我").start();        new Thread(station,"牛逼的你们").start();        new Thread(station,"可恶的黄牛党").start();    }    }class BuyTicket implements Runnable{        //票    private int ticketNum = 10;    boolean flag = true; //内部进行形式    @Override    public void run() {        //买票        while(flag) {            buy();        }    }    private void buy() {        if(ticketNum<=0) {            flag = false;            return;        }        try {            Thread.sleep(100);        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        //买票        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNum--);    }    }

2.不平安的取钱

//不平安的取钱(两个人)public class UnsafeBank {    public static void main(String[] args) {        Account account = new Account(100,"结婚基金");        Drawing you = new Drawing(account,50,"你");        Drawing GF = new Drawing(account,100,"女朋友");        you.start();        GF.start();    }}//账户class Account{    int money; //余额    String name; //卡名    public Account(int money, String name) {        super();        this.money = money;        this.name = name;    }}//银行:模仿取款class Drawing extends Thread{    Account account;    //取了多少钱    int drawingMoney;    //当初手里有多少钱    int nowMoney;    public Drawing(Account account,int drawingMoney,String name) {        super(name);        this.account = account;        this.drawingMoney = drawingMoney;    }    //取钱    @Override    public void run() {        //判断有没有钱        if(account.money - drawingMoney < 0) {            System.out.println(Thread.currentThread().getName()+"钱不够,取不了");            return;        }        //放大问题的产生性        try {            Thread.sleep(100);        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        //卡内余额 = 余额 - 取的钱        account.money = account.money - drawingMoney;        //你手里的钱        nowMoney = nowMoney + drawingMoney;        System.out.println(account.name+"余额为:"+account.money);        //Thread.currentThread() == this.getName()        System.out.println(this.getName()+"手里的钱:"+nowMoney);    }}

3.不平安的汇合

//线程不平安的汇合//增加到同一地位被笼罩public class UnsafeList {    public static void main(String[] args) {        List<String> list = new ArrayList<String>();        for (int i = 0; i < 10000; i++) {            new Thread(()->{                list.add(Thread.currentThread().getName());            }).start();        }        System.out.println(list.size());    }}

synchronized

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

synchronized办法管制对“对象”的拜访,每个对象对象对应一把锁,每个synchronized办法都必须取得调用该办法的锁能力执行,否则线程会阻塞,办法一旦执行,就独占该锁,直到该办法返回才开释锁,前面被阻塞的线程能力取得这个锁,继续执行

同步块:synchronized(Obj) { }

Obj称为同步监视器

  • Obj能够是任何对象,然而举荐应用共享资源作为同步监视器
  • 同步办法中无需指定同步监视器,因为同步办法的同步监视器就是this
public void run() {        //锁的对象就是变动的量,须要增删改的对象        synchronized (account) {            //判断有没有钱            if(account.money - drawingMoney < 0) {                System.out.println(Thread.currentThread().getName()+"钱不够,取不了");                return;            }            //放大问题的产生性            try {                Thread.sleep(100);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            //卡内余额 = 余额 - 取的钱            account.money = account.money - drawingMoney;        }        //你手里的钱        nowMoney = nowMoney + drawingMoney;        System.out.println(account.name+"余额为:"+account.money);        //Thread.currentThread() == this.getName()        System.out.println(this.getName()+"手里的钱:"+nowMoney);    }

CopyOnWriteArrayList

JUC就是java.util .concurrent工具包的简称。这是一个解决线程的工具包,JDK 1.5开始呈现的。Callable接口也在JUC中。

import java.util.concurrent.CopyOnWriteArrayList;//测试JUC平安类型的汇合public class TestJUC {    public static void main(String[] args) {        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();        for(int i = 0; i < 10000; i++) {            new Thread(()->{                list.add(Thread.currentThread().getName());            }).start();        }        try {            Thread.sleep(3000);        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        System.out.println(list.size());    }}

1.4.2 死锁

多个线程各自占有一些共享资源,并且相互期待其余线程占有的资源能力运行,而导致两个或者多个线程都在期待对方开释资源,都进行执行的情景,某一同步块同时领有”两个以上对象的锁“时,就可能会产生”死锁“的问题

//死锁:多个线程相互抱着对方须要的资源,而后造成僵持public class DeadLock {    public static void main(String[] args) {        Makeup g1 = new Makeup(0,"灰姑娘");        Makeup g2 = new Makeup(1,"白雪公主");        g1.start();        g2.start();    }}//口红class Lipstick{    }//镜子class Mirror{    }class Makeup extends Thread{    //须要的资源只有一份,用static来保障只有一份    static Lipstick lipstick = new Lipstick();    static Mirror mirror = new Mirror();        int choice; //抉择    String girlName; //抉择化妆品的人        Makeup(int choice,String girlName){        this.choice = choice;        this.girlName = girlName;    }    @Override    public void run() {        //化妆        try {            makeup();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }        //化妆,相互持有对方的锁,就是须要拿到对方的资源    private void makeup() throws InterruptedException {        if(choice == 0) {            synchronized(lipstick) { //取得口红的锁                System.out.println(this.girlName+"取得口红的锁");                Thread.sleep(1000);                            synchronized(mirror) {                    System.out.println(this.girlName+"取得镜子的锁");                }            }        }else {            synchronized(mirror) { //取得口红的锁                System.out.println(this.girlName+"取得镜子的锁");                Thread.sleep(2000);                            synchronized(lipstick) {                    System.out.println(this.girlName+"取得口红的锁");                }            }        }    }}

锁中锁(多个对象相互嵌套的锁)使多个线程相互抱着对方须要的资源,而后造成僵持

解决办法:锁离开写,不要同时占有多个资源

//化妆,相互持有对方的锁,就是须要拿到对方的资源    private void makeup() throws InterruptedException {        if(choice == 0) {            synchronized(lipstick) { //取得口红的锁                System.out.println(this.girlName+"取得口红的锁");                Thread.sleep(1000);            }            synchronized(mirror) {                    System.out.println(this.girlName+"取得镜子的锁");                }        }else {            synchronized(mirror) { //取得口红的锁                System.out.println(this.girlName+"取得镜子的锁");                Thread.sleep(2000);            }            synchronized(lipstick) {                    System.out.println(this.girlName+"取得口红的锁");                }        }

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

  1. 互斥条件
  2. 申请与放弃条件
  3. 不剥夺条件
  4. 循环期待条件

1.4.3 Lock锁

从JDK 5.0开始,Java提供了更弱小的线程同步机制——显式定义同步锁对象来实现同步。同步锁应用Lock对象充当,Lock锁也蕴含在JUC内

ReentrantLock(可重入锁)类实现了Lock,能够显式加锁、开释锁

应用格局:

Lock lock=new ReentrantLock();lock.lock();try{    //解决工作}catch(Exception ex){     }finally{    lock.unlock();   //开释锁}

测试Lock类(买票):

import java.util.concurrent.locks.ReentrantLock;//测试Lock类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;        //定义Lock锁    private final ReentrantLock lock = new ReentrantLock();    @Override    public void run() {        while(true) {            lock.lock();//加锁            try {                if(ticketNum > 0) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                    System.out.println(ticketNum--);                }else {                    break;                }            }finally {                //解锁                lock.unlock();            }                }    }}

synchronized与Lock比照

  • Lock是显式锁(手动开启和敞开锁),synchronized是隐式锁,主动开释
  • 应用Lock锁,JVM将破费较少的工夫来调度线程,性能更好。并且具备更好的扩展性(提供更多子类)

1.4.4 线程通信

线程通信办法:

留神:均是Object类的办法,都只能在同步办法或者同步代码块中应用,否则会抛出异样

生产者消费者问题

解决办法:

  1. 利用缓冲区解决:管程法

    //测试生产者消费者模型-->利用缓冲区解决:管程法//生产者,消费者,产品,缓冲区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 = 0; i < 100; i++) {            container.push(new Chicken(i));            System.out.println("生产了第"+i+"只鸡");        }    }}//消费者class Consumer extends Thread{    SynContainer container;    public Consumer(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) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        //如果没有满,咱们就须要丢入产品        chickens[count] = chicken;        count++;                //能够告诉消费者生产了        this.notifyAll();    }    //消费者生产产品    public synchronized Chicken pop() {        //判断是否生产        if(count==0) {            //期待生产者生产            try {                this.wait();            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        //如果能够生产        count--;        Chicken chicken = chickens[count];            //吃完了,告诉生产者生产        this.notifyAll();        return chicken;            }}
  2. 利用标记位解决:信号灯法

    package com.zhg.thread;//测试生产者消费者模型2-->利用标记位解决:信号灯法public class TestPC2 {    public static void main(String[] args) {        TV tv = new TV();        new Player(tv).start();        new Watcher(tv).start();    }}//生产者-->演员class Player extends Thread{    TV tv;    public Player(TV tv) {        this.tv = tv;    }    @Override    public void run() {        for (int i = 0; i < 20; i++) {            if(i%2==0) {                this.tv.play("高兴大本营");            }else {                this.tv.play("广告");            }        }    }}//消费者-->观众class Watcher extends Thread{    TV tv;    public Watcher(TV tv) {        this.tv = tv;    }    @Override    public void run() {        for (int i = 0; i < 20; i++) {            tv.watch();        }    }}//产品-->节目class TV{        //演员表演,观众期待 T    //观众观看,演员期待 F    String voice;//表演的节目    boolean flag = true;        //表演    public synchronized void play(String voice) {                if(!flag) {            try {                this.wait();            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }                System.out.println("演员表演了:"+voice);        //告诉观众观看        this.notifyAll(); //告诉唤醒        this.voice = voice;        this.flag = !this.flag;    }        //观看    public synchronized void watch() {        if(flag) {            try {                this.wait();            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        System.out.println("观众观看了:"+voice);        //告诉演员表演        this.notifyAll();        this.flag = !this.flag;    }}

线程池

背景:常常创立和销毁,使用量特地大的资源,比方并发状况下的线程,对性能影响很大。

思路:提前创立好多个线程,放入线程池中,应用时间接获取,应用完放回池中。能够防止频繁创立销毁、实现反复利用。

益处:

  • 进步响应速度
  • 升高资源耗费
  • 便于线程治理

JDK 5.0起提供了线程池相干API:ExecutorService和Executors

  • ExecutorService:真正的线程池接口。
  • Executor:工具类、线程池的工厂类,用于创立并返回不同类型的线程池。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;//测试线程池public class TestPool {    public static void main(String[] args) {        //1.创立服务,创立线程池        //newFixedThreadPool 参数为线程池大小        ExecutorService service = Executors.newFixedThreadPool(10);        service.execute(new MyThread());        service.execute(new MyThread());        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());            }}