Java中的多线程

三种创建方式

  • Thread class 继承Thread类(重点)

  • Runnable接口

  • Callable接口

Thread

  • 自定义一个线程类继承Thread类

  • 重写一个run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动

     public class ThreadTest extends Thread{
         @Override
         public void run() {
             for (int i = 0; i < 100; i++) {
                 System.out.println("重写线程方法!");
            }
        }
     
         public static void main(String[] args) {
             ThreadTest threadTest = new ThreadTest();
             threadTest.start();
             for (int i = 0; i < 1000; i++) {
                 System.out.println("执行主线程");
            }
        }
     }

网图下载

 public class TestThread extends Thread {
     private String url;//网络图片地址
     private String name;//保存的图片名
     //构造器
     public TestThread(String url,String name){
         this.url=url;
         this.name=name ;
    }
 
 //重写run执行体
     @Override
     public void run() {
         WebDownloader webDownloader = new WebDownloader();
         webDownloader.downloader(url,name);
         System.out.println("文件名叫:"+name);
    }
 //
     public static void main(String[] args) {
         TestThread t1 = new TestThread("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","one.jpg");
         TestThread t2 = new TestThread("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","two.jpg");
         TestThread t3 = new TestThread("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","there.jpg");
         t1.start();
         t2.start();
         t3.start();
    }
 }
 //下载类(导入commons-io jar包 依赖 --> 才有FileUtil)
 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异常,download出错!");
        }
    }
 }
  • 子类继承Thread类具备多线程能力

  • 启动线程:子类对象.start();

  • 不建议使用:避免OOP单继承局限性

    OOP全称Object Oriented Programming,是指面向对象程序设计,是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单shu个能够起到子程序作用的单元或对象组合而成。OOP的优缺点:

    1、OOP 的优点:使人们的编程与实际的世界更加接近,所有的对象被赋予属性和方法,结果编程就更加富有人性化。

    2、OOP 的也有缺点,就 C++ 而言,由于面向更高的逻辑抽象层,使得 C++ 在实现的时候,不得不做出性能上面的牺牲,有时候甚至是致命的 。

实现Runnable接口

  • 自定义一个线程类实现Runnable接口

  • 重写一个run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动

 public class TestThread implements Runnable {
     private String url;//网络图片地址
     private String name;//保存的图片名
     public TestThread(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) {
         TestThread t1 = new TestThread("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","one.jpg");
         TestThread t2 = new TestThread("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","two.jpg");
         TestThread t3 = new TestThread("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","there.jpg");
         new Thread(t1).start();
         new Thread(t2).start();
         new Thread(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异常,download出错!");
        }
    }
 }
  • 实现接口Runnable具有多线程能力

  • 启动线程:new Thread对象+传入目标对象.start();

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

多并发

 //问题:多线程操作同一个资源的情况下,出现资源紊乱,线程不安全
 public class TestThread implements Runnable {
     private Integer ticketNum = 10;//车票
 
     @Override
     public void run() {
         while(true){
             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) {
         TestThread ticket = new TestThread();
         new Thread(ticket,"小李").start();
         new Thread(ticket,"国服").start();
         new Thread(ticket,"岁岁").start();
    }
 }

龟兔赛跑--RACE

 public class Race implements Runnable{
     private static String winner;
     @Override
     public void run() {
         //模拟跑步
         for (int i = 0; i <= 100; i++) {
             //模拟兔子睡觉
             if (Thread.currentThread().getName().equals("兔子") && i%10==0){
                 try {
                     Thread.sleep(2);
                } 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 false;
    }
 
     public static void main(String[] args) {
         Race race = new Race();
         new Thread(race,"兔子").start();
         new Thread(race,"乌龟").start();
    }
 
 }
 

实现Callable接口

  • 实现Callable接口,需要返回值类型

  • 重写call方法,需要 抛出异常

  • 创建目标对象

  • 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);

  • 提交执行:Future<Boolean> result1 = ser.submit(t1);

  • 获取结果:boolean r1 = result1.get();

  • 关闭服务:ser.shutdownNow();

 public class TestCallable implements Callable {
     private String url;//网络图片地址
     private String 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 ExecutionException, InterruptedException {
         TestCallable t1 = new TestCallable("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","one.jpg");
         TestCallable t2 = new TestCallable("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","two.jpg");
         TestCallable t3 = new TestCallable("http://img03.sogoucdn.com/v2/thumb/retype/ext/auto/q/90/?appid=100140019&url=https%3A%2F%2Fimg3.doubanio.com%2Fview%2Fphoto%2Fs_ratio_poster%2Fpublic%2Fp2520456502.jpg","there.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.shutdownNow();
    }
 }
 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异常,download出错!");
        }
    }
 }
 

静态代码模式

静态代码模式总结:

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

  • 代理对象要代理真实角色

  • 好处

    • 代理对象可以做很多真实对象做不了的事情

    • 真实对象专注做自己的事情

 public class StaticProxy {
     public static void main(String[] args) {
         WeddingCompany weddingCompany = new WeddingCompany(new You());
         weddingCompany.HappyMarry();
    }
 }
 interface Marry{
     //人间四大喜事
     //久旱逢甘霖 他乡遇故知 洞房花烛夜 金榜题名时
     void HappyMarry();
 }
 //真实角色,你去结婚
 class You implements Marry{
     @Override
     public void HappyMarry() {
         System.out.println("XXX要结婚了!开心!");
    }
 }
 //代理角色,帮助你结婚
 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("结婚前布置现场");
    }
 }

Lambda表达式

  • 避免匿名内部类定义过多

  • 其实质属于函数式编程概念

  • 函数式接口

    • 任何接口,如果只包含一个抽象类,那么它就是函数式接口

    • 对于函数式接口我们可以用Lambda表达式来创建接口对象

推导Lambda表达式

 public class TestLambda {
     //静态内部类
     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();
         //局部内部类
         class Like3 implements ILike{
             @Override
             public void lambda() {
                 System.out.println("i like lambda3");
            }
        }
         like = new Like3();
         like.lambda();
         //匿名内部类
         like = new ILike() {
             @Override
             public void lambda() {
                 System.out.println("i like lambda4");
            }
        };
         like.lambda();
         //lambda表达式
         like=()->{
             System.out.println("I like lambda5");
        };
         like.lambda();
    }
 }
 //定义一个函数接口
 interface ILike{
     void lambda();
 }
 //实现类
 class Like implements ILike{
     @Override
     public void lambda() {
         System.out.println("i like lambda");
    }
 }

线程五大状态

 

线程方法

方法说明
setPriority(int new Priority) 更改线程的优先级
static void sleep(long mills) 在指定的毫秒数内让当前正在执行的线程体休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程,别用这个方法
boolean isAlive() 测试线程是否属于活动状态

停止线程

  • 不推荐使用JDK提供的stop()、destory()方法。

  • 推荐使用自己停下来

  • 建议使用一个标志位进行终止变量,当flag=false,线程停止

 public class TestStop implements Runnable{
     //设置一个标志位
     private boolean flag = true;
     @Override
     public void run() {
         int i=0;
         while(flag){
             System.out.println("线程进行中"+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 < 1000; i++) {
             System.out.println("主线程"+i);
             if (i==900){
                 testStop.Stop();
                 System.out.println("线程停止!");
            }
        }
    }
 }

线程休眠

  • sleep(时间)指定当前线程阻塞的毫秒差

  • sleep存在异常InterruptedException;

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

  • sleep可以模拟网络延时,倒计时等;

  • 每一个对象都有一个锁,sleep不会释放锁

 public class TestSleep implements Runnable {
         private Integer ticketNum = 10;//车票
         @Override
         public void run() {
             while(true){
                 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) {
             TestSleep ticket = new TestSleep();
             new Thread(ticket,"小李").start();
             new Thread(ticket,"国服").start();
             new Thread(ticket,"岁岁").start();
        }
 }
 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;
            }
        }
    }
 }
 public class TestSleep {
     public static void main(String[] args) {
         //打印系统时间
         Date startTime = new Date(System.currentTimeMillis());
         while (true){
             try {
                 Thread.sleep(1000);
                 System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                 startTime = new Date(System.currentTimeMillis());//更新系统时间
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
    }
 }
 

线程礼让

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

  • 让线程从运行状态变为就绪状态

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

 public class TestYield {
     public static void main(String[] args) {
         txtYield txtYield = new txtYield();
         new Thread(txtYield,"a").start();
         new Thread(txtYield,"b").start();
    }
 }
 class txtYield implements Runnable{
     @Override
     public void run() {
         System.out.println(Thread.currentThread().getName()+"线程执行");
         Thread.yield();//线程礼让
         System.out.println(Thread.currentThread().getName()+"线程停止");
    }
 }

线程强制执行

  • Join合并线程,待此线程执行完毕后再执行其他线程,其他线程阻塞

  • 可以理解为VIP不用排队

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

线程状态观测

  • 线程状态

    线程可以处于以下状态之一:

    • NEW

      尚未启动的线程处于此状态。

    • RUNNABLE

      在Java虚拟机中执行的线程处于此状态。

    • BLOCKED

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

    • WAITING

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

    • TIMED_WAITING

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

    • TERMINATED

      已退出的线程处于此状态。

 public class TestThreadState {
     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);//new
         //观察启动后
         thread.start();
         state = thread.getState();
         System.out.println(state);//run
         //只要线程不终止就一直输出状态
         while (state!=Thread.State.TERMINATED){
             Thread.sleep(100);
             state = thread.getState();
             System.out.println(state);
        }
    }
 }

线程的优先级

 public class TestPriority {
     public static void main(String[] args) {
         //主线程默认优先级
         System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
         MyPriority myPriority = new MyPriority();
         Thread thread1 = new Thread(myPriority,"a");
         Thread thread2 = new Thread(myPriority,"b");
         Thread thread3 = new Thread(myPriority,"c");
         Thread thread4 = new Thread(myPriority,"d");
         Thread thread5 = new Thread(myPriority);
         Thread thread6 = new Thread(myPriority);
         //先设置优先级,再启动
         thread1.start();
         thread2.setPriority(1);
         thread2.start();
         thread3.setPriority(3);
         thread3.start();
         thread4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
         thread4.start();
    }
 }
 class MyPriority implements Runnable{
     @Override
     public void run() {
         System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
 }

守护线程Daemon

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

  • 虚拟机必须确保用户线程执行完毕

  • 虚拟机不用等待守护线程执行完毕

  • 像后台记录操作日志,监控录像,垃圾回收等

 public class TestDaemon {
     public static void main(String[] args) {
         God god = new God();
         YouLive you = new YouLive();
         Thread thread = new Thread(god);
         thread.setDaemon(true);//默认是false,默认是用户线程
         thread.start();//上帝线程
         new Thread(you).start();//you
    }
 }
 class God implements Runnable{
     @Override
     public void run() {
         while (true){
             System.out.println("上帝保佑你!");
        }
    }
 }
 class YouLive implements Runnable{
     @Override
     public void run() {
         for (int i = 0; i < 36500; i++) {
             System.out.println("你快乐的活着!");
        }
         System.out.println("====你死了!====");
    }
 }

线程同步机制

  • 并发:同一对象被多个线程同时操作

  • 线程同步其实是一个等待机制,多个需要同时访问这个对象的线程进入这个对象的等待池进行排列

  • 队列和锁

    • 锁机制:synchronized

    • 加锁引起延时等性能问题,也会导致性能倒置

三大不安全案例

 public class UnsafeTicket {
     public static void main(String[] args) {
         BuyTicket buyTicket = new BuyTicket();
         new Thread(buyTicket,"我").start();
         new Thread(buyTicket,"你").start();
         new Thread(buyTicket,"黄牛").start();
 
    }
 }
 class BuyTicket implements Runnable{
     private int ticketNums = 10;//票数
     private boolean flag = true;
     //买票
     private void buy(){
         if (ticketNums<=0){
             flag = false;
             return;
        }
         System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--);
    }
     @Override
     public void run() {
         while (flag){
             buy();
             try {
                 Thread.sleep(10);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
    }
 }
 public class UnsafeBank {
     public static void main(String[] args) {
         Account acc = new Account(100, "育儿基金");
         Bank you = new Bank(acc, "你", 50);
         Bank girlFriend = new Bank(acc, "女朋友", 100);
         you.start();
         girlFriend.start();
    }
 }
 //模拟账户
 class Account{
     int money;
     String name;
 
     public Account(int money, String name) {
         this.money = money;
         this.name = name;
    }
 }
 //模拟银行
 class Bank extends Thread{
     Account account;
     int handMoney;
     int drawingMoney;
     public Bank(Account account, String name, int drawingMoney) {
         super(name);
         this.account = account;
         this.drawingMoney = drawingMoney;
    }
 //判断取钱模式
     @Override
     public void run() {
         if (account.money - drawingMoney<=0){
             System.out.println(Thread.currentThread().getName()+"钱不够了");
        }
         account.money = account.money - drawingMoney;//余额更新
         handMoney = handMoney + drawingMoney;//手里的钱更新
         System.out.println(account.name+"余额为:"+account.money);
         System.out.println(this.getName()+"手里的钱"+handMoney);
    }
 }
 //线程不安全的集合
 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();
        }
         try {
             Thread.sleep(2000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
         System.out.println(list.size());
    }
 }

同步方法及同步块

如上代码加锁

 public class UnsafeTicket {
     public static void main(String[] args) {
         BuyTicket buyTicket = new BuyTicket();
         new Thread(buyTicket,"我").start();
         new Thread(buyTicket,"你").start();
         new Thread(buyTicket,"黄牛").start();
 
    }
 }
 class  BuyTicket implements Runnable{
     private int ticketNums = 10;//票数
     private boolean flag = true;
     //买票
     private synchronized void buy(){
         if (ticketNums<=0){
             flag = false;
             return;
        }
         System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--);
    }
     @Override
     public void run() {
         while (flag){
             buy();
             try {
                 Thread.sleep(10);
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
    }
 }
 public class UnsafeBank {
     public static void main(String[] args) {
         Account acc = new Account(100, "育儿基金");
         Bank you = new Bank(acc, "你", 50);
         Bank girlFriend = new Bank(acc, "女朋友", 100);
         you.start();
         girlFriend.start();
    }
 }
 //模拟账户
 class Account{
     int money;
     String name;
     public Account(int money, String name) {
         this.money = money;
         this.name = name;
    }
 }
 //模拟银行
 class Bank extends Thread{
     Account account;
     int handMoney;
     int drawingMoney;
     public Bank(Account account, String name, int drawingMoney) {
         super(name);
         this.account = account;
         this.drawingMoney = drawingMoney;
    }
 //判断取钱模式
     @Override
     public void run() {
         synchronized (account){
             if (account.money - drawingMoney<=0){
                 System.out.println(Thread.currentThread().getName()+"钱不够了");
                 return;
            }
             account.money = account.money - drawingMoney;//余额更新
             handMoney = handMoney + drawingMoney;//手里的钱更新
             System.out.println(account.name+"余额为:"+account.money);
             System.out.println(this.getName()+"手里的钱"+handMoney);
        }
        }
 
 }
 //方法块没加上
 public class unsafeList {
     public static void main(String[] args) {
         List<String> list = new ArrayList<String>();
         for (int i = 0; i <= 10000; i++) {
             new Thread(()->{
                 synchronized (list){
                     list.add(Thread.currentThread().getName());
                }
            }).start();
        }
         try {
             Thread.sleep(3000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
         System.out.println(list.size());
    }
 }

CopyOnWriteArrayList

死锁

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

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

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

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

 public class DeadLock {
     public static void main(String[] args) {
         MakeUp mak1 = new MakeUp(0, "雪儿");
         MakeUp mak2 = new MakeUp(1, "紫儿");
         mak1.start();
         mak2.start();
    }
 }
 class Lipstick{}//口红
 class Mirror{}//镜子
 class MakeUp extends Thread{
     //确保资源只有一份
     static Lipstick lipstick = new Lipstick();
     static Mirror mirror = new Mirror();
     int choice;
     String girl;
     MakeUp(int choice,String girl){
         this.choice=choice;
         this.girl=girl;
    }
     @Override
     public void run() {
         //化妆
         try {
             makeup();
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
    }
     private void makeup() throws InterruptedException {
         if (choice==0){
             synchronized (lipstick){
                 System.out.println(this.girl+"口红");
                 Thread.sleep(100);
                 synchronized (mirror){
                     System.out.println(this.girl+"镜子");
                }
            }
        }else {
             synchronized (mirror){
             System.out.println(this.girl+"镜子");
             Thread.sleep(100);
             synchronized (lipstick){
                 System.out.println(this.girl+"口红");
            }
        }
        }
    }
 }

Lock锁

  • 从JDK5.0开始,Java提供了更加强大的线程同步机制--通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当

  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象

  • ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁、释放锁

 public class TestLock {
     public static void main(String[] args) {
         TestLock2 testLock2 = new TestLock2();
         new Thread(testLock2).start();
    }
 
 }
 class TestLock2 implements Runnable{
     int ticketNums=10;
     //定义Lock
     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;
                }
            }finally {
                 lock.unlock();//释放锁
            }
        }
    }
 }

生产者和消费者的问题

线程通信:

  • 应用场景:生产者------>仓库------>消费者

  • 分析,生产者和消费者共享一个资源,并相互依赖,互为条件。

  • 在这个问题中,仅仅用synchronized是不够的

    • synchronized可阻止并发更新同一个共享资源,实现了同步

    • synchronized不能用来实现不同线程之间的信息传递(通信)

管程法

 //测试:生产消费者模型-->利用缓冲区解决:管程法
 //生产者 消费者 缓存区 产品
 public class TestPC {
     public static void main(String[] args) {
         SynContainer synContainer = new SynContainer();
         new Producer(synContainer).start();
         new Consumer(synContainer).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 Product(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 Product{
     int id;
     public Product(int id) {
         this.id = id;
    }
 }
 //缓存区
 class SynContainer{
     //需要一个容器
     Product[] products = new Product[10];
     //容器计数器
     int count = 0;
     //生产者放入产品
     public synchronized void push(Product product){
         //如果产品满了就等待消费者消费
         if (count==products.length){
             try {
                 this.wait();
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
         //如果没满,我们就丢入产品
         products[count]=product;
         count++;
         //通知消费者消费
         this.notifyAll();
    }
     //消费者消费
     public synchronized Product pop(){
         //判断是否可以消费
         if (count==0){
             try {
                 this.wait();
            } catch (InterruptedException e) {
                 e.printStackTrace();
            }
        }
         //可以消费
         count--;
         Product product = products[count];
         //吃完了通知生产者
         this.notifyAll();
         return product;
    }
 }

信号灯法

 //信号灯法,标志位解决
 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++) {
             this.tv.watch();
        }
    }
 }
 //产品-->节目
 class TV{
     String voice;//节目
     boolean flag = true;
     //表演
     public synchronized void play(String voice){
         if (!flag){
             try {
                 this.wait();
            } catch (InterruptedException e) {
                 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) {
                 e.printStackTrace();
            }
        }
         System.out.println("观看了"+voice);
         //通知表演
         this.notifyAll();
         this.flag=!this.flag;
    }
 }

线程池

  • JDK5.0起提供了线程池相关的API:ExecutorService和Executors

  • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

    • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable

    • <T>Future<T>submit(Callable<T>task):执行任务,有返回值,一般又来执行Callable

    • void shutdown():关闭连接池

  • Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

 //测试线程池
 public class TestLoop {
     public static void main(String[] args) {
         //创建服务,创建线程池
         //Executors.newFixedThreadPool 参数是线程池大小
         ExecutorService service = Executors.newFixedThreadPool(10);
         //执行一个Runnable线程
         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());
    }
 }

 

posted @ 2021-03-10 09:02  keepClam  阅读(138)  评论(0)    收藏  举报