首页 > 编程语言 > 详细

JAVA多线程

时间:2021-03-07 00:05:42      阅读:27      评论:0      收藏:0      [点我收藏+]

JAVA多线程

  • 程序

  • 进程

  • 线程

  • 多线程

  • 并发

main()主线程

创建线程

  • 继承Thread

单继承局限性
  • 实现Runnable接口

避免单继承局限性, 灵活  方便同一个对象被多个线程调用
  • 实现Callable接口

package com.cyz.demo01;
?
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();
?
       //直接调用run()会先执行run()
       //testThread1.run();
       //调用start() 方法开启线程   由cpu调度
       testThread1.start();
?
       for (int i = 0; i < 200; i++){
           System.out.println("我在学习多线程!"+i);
      }
  }
}
?
  • 导入commons-io-2.6.jar

  • 模拟同步下载图片

package com.cyz.demo01;
?
import org.apache.commons.io.FileUtils;
?
import java.io.File;
import java.io.IOException;
import java.net.URL;
?
?
//练习Thread 实现多线程同步下载图片
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://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2488102644,4095521058&fm=26&gp=0.jpg", "logo1.jpg");
       TestThread2 t2 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1590680125925&di=f56419579f92cece858fb47eb8d3b831&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20180703%2F4669d3d7dec3494ebcd87f342ba9dc1b.jpeg", "logo2.jpg");
       TestThread2 t3 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1590680125925&di=1ea492a995c25217247505465f23309a&imgtype=0&src=http%3A%2F%2Fa.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2F9358d109b3de9c82713ecb576c81800a18d8438a.jpg", "logo3.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异常,downloader方法出现异常");
      }
  }
}
?
  • 实现Runnable接口

package com.cyz.demo01;
?
//方式2 实现Runnable接口, 重写run方法
   //需要Thread代理
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) {
       TestThread3 testThread3 = new TestThread3();
?
       //代理 传入实现接口的对象
       new Thread(testThread3).start();
?
       for (int i = 0; i < 200; i++) {
           System.out.println("我在学习多线程!"+i);
      }
?
  }
}
?
  • 多个线程操作同一个对象

package com.cyz.demo01;
?
//有并发问题 同时会拿到相同的数字
public class TestThread4 implements Runnable {
?
  //票数
  private int ticketNums = 10;
?
  @Override
  public void run() {
      while (true){
          //延时
          try {
              Thread.sleep(200);
              if (ticketNums <= 0){
                  break;
              }
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
?
          System.out.println(Thread.currentThread().getName()+" 拿到了第"+ ticketNums-- +" 张票");
      }
  }
?
  public static void main(String[] args) {
      TestThread4 ticket = new TestThread4();
?
      new Thread(ticket,"小名").start();
      new Thread(ticket,"老师").start();
      new Thread(ticket,"黄牛").start();
  }
}
?

龟兔赛跑案例

package com.cyz.demo01;
?
import sun.awt.windows.ThemeReader;
?
//模拟龟兔赛跑
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(1);
              } 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;
  }
?
   public static void main(String[] args) {
       Race race = new Race();
?
       new Thread(race,"兔子").start();
       new Thread(race,"乌龟").start();
  }
}
?
  • 实现callable接口

  • 重写call方法

  • 可以定义返回值

  • 可以抛出异常

package com.cyz.demo02;
?
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 {
   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 c1 = new TestCallable("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2488102644,4095521058&fm=26&gp=0.jpg", "logo1.jpg");
       TestCallable c2 = new TestCallable("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1590680125925&di=f56419579f92cece858fb47eb8d3b831&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20180703%2F4669d3d7dec3494ebcd87f342ba9dc1b.jpeg", "logo2.jpg");
       TestCallable c3 = new TestCallable("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1590680125925&di=1ea492a995c25217247505465f23309a&imgtype=0&src=http%3A%2F%2Fa.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2F9358d109b3de9c82713ecb576c81800a18d8438a.jpg", "logo3.jpg");
?
       //创建执行服务
       ExecutorService ser = Executors.newFixedThreadPool(3);
?
       //提交执行
       Future<Boolean> r1 = ser.submit(c1);
       Future<Boolean> r2 = ser.submit(c2);
       Future<Boolean> r3 = ser.submit(c3);
       //获得结果
       boolean result1 = r1.get();
       boolean result2 = r2.get();
       boolean result3 = r3.get();
       //关闭服务
       ser.shutdownNow();
?
       System.out.println("result1"+result1);
       System.out.println("result2"+result2);
       System.out.println("result3"+result3);
  }
?
}
?
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异常,downloader方法出现异常");
      }
  }
}
?

静态代理模式

  • 真实对象和代理对象实现用一个接口

  • 代理对象代理真实角色

  • 代理可以有自己的业务

package com.cyz.demo03;

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("结婚啦");
}

}

//代理
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("结婚前,布置现场");
}

}

  • 同样Thread也是作为代理, 实现的是run方法

new Thread(()->System.out.println("我爱你")).start();

new WeddingCompany(new You().HappyMarry();

Lambda表达式

  • 函数式编程

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

(params) -> 表达式
    (params) -> 语句
    	(params) -> {}

前提

  • 接口只有一个抽象方法

  •  

函数式接口

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

public inteface Runnable{
    public void run();
}

演化过程

package com.cyz.demo03;

public class TestLambda {
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
}
}

//函数式接口
interface ILike{
void lambda();
}

//实现类
class Like implements ILike{

@Override
public void lambda() {
    System.out.println("I like lambda");
}

}

package com.cyz.demo03;

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();
    }
}

//函数式接口
interface ILike{
    void lambda();
}

//实现类
class Like implements ILike{

    @Override
    public void lambda() {
        System.out.println("I like lambda");
    }
package com.cyz.demo03;

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();
        
    }
}

//函数式接口
interface ILike{
    void lambda();
}

//实现类
class Like implements ILike{

    @Override
    public void lambda() {
        System.out.println("I like lambda");
    }
}


package com.cyz.demo03;

/**
 * com.cyz.demo03
 *
 * @author cyz
 * @create 2020/05/29 09-05
 */
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();

    }
}

//函数式接口
interface ILike{
    void lambda();
}

//实现类
class Like implements ILike{

    @Override
    public void lambda() {
        System.out.println("I like lambda");
    }
}


package com.cyz.demo03;

public class TestLambda {

    //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");
    }
}


例子

package com.cyz.demo03;

public class TestLambda2 {
public static void main(String[] args) {
ILove iLove = (int a) -> {
System.out.println(a);
};

    iLove.love(1);

    //简化
    //多参数 一个去掉参数类型, 就都去掉
    iLove = (a) -&gt;{
        System.out.println(a);
    };

    iLove.love(2);

    //在简化
	//多个参数必须有括号
    iLove = a -&gt;{
        System.out.println(a);
    };

    iLove.love(3);

    //在简化   前提:代码只有一行
    iLove = a -&gt; System.out.println(a);
    
    iLove.love(4);
}

}

interface ILove{
void love(int a);
}

线程转态

  1. 创建状态 (new)

  2. 就绪状态 (调用start()后)

  3. 阻塞状态 (调用 sleep, wait 或 同步锁时)

  4. 运行状态 (CPU调度)

  5. 死亡状态 (中断 或 结束)

线程方法

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

停止线程

  1. 建议线程正常停止 --->利用次数, 不建议死循环

  2. 建议使用标志位 ----> 设置一个标志位

  3. 不使用stop 或者 destroy 过时了

package com.cyz.demo04;

//测试stop
//1. 建议线程正常停止 --->利用次数, 不建议死循环
//2. 建议使用标志位 ----> 设置一个标志位
//3. 不使用stop 或者 destroy 过时了
public class TestStop implements Runnable {

//1.设置一个标志位
private boolean flag = true;

@Override
public void run() {
    int i = 0;
    while (flag){
        System.out.println("run---"+i++);
    }
}

//2. 设置一个公开的方法停止线程 转换标志位
public void stop(){
    this.flag = false;
}

public static void main(String[] args) {
    TestStop stop = new TestStop();

    new Thread(stop).start();

    for (int i = 0; i &lt; 1000; i++){
        System.out.println("main"+i);
        if(i == 900){
            stop.stop();
            System.out.println("线程停止了");
        }
    }
}

}

Sleep

  1. 时间为毫秒数

  2. 存在异常InterruptedException

  3. 时间达到后线程进入就绪状态

  4. 模拟网络延时, 倒计时

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

延时

package com.cyz.demo04;

/**

  • com.cyz.demo04

  • @author cyz

  • @create 2020/05/29 09-43
    */
    public class TestSleep implements Runnable{

    //票数
    private int ticketNums = 10;

    @Override
    public void run() {
    while (true){
    //延时
    try {
    Thread.sleep(200);
    if (ticketNums <= 0){
    break;
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

         System.out.println(Thread.currentThread().getName()+" 拿到了第"+ ticketNums-- +" 张票");
     }
    

    }

    public static void main(String[] args) {
    TestSleep ticket = new TestSleep();

     new Thread(ticket,"小名").start();
     new Thread(ticket,"老师").start();
     new Thread(ticket,"黄牛").start();
    

    }
    }

倒计时

package com.cyz.demo04;

public class testSleep2 {

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 &lt;= 0){
            break;
        }
    }
}

}

模拟打印系统时间

package com.cyz.demo04;

import java.text.SimpleDateFormat;
import java.util.Date;

public class testSleep2 {

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();
        }
    }
}

}

线程礼让 -yield

  1. 让当前正在执行的线程暂停, 但不阻塞

  2. 将线程运行状态转为就绪状态

  3. 让cpu重新调度, 礼让不一定成功, 得看cpu

package com.cyz.demo05;

public class TestYield {

public static void main(String[] args) {
    MyYield myYield = new MyYield();

    new Thread(myYield,"A").start();
    new Thread(myYield,"B").start();
}

}

class MyYield implements Runnable{

@Override
public void run() {
    System.out.println(Thread.currentThread().getName()+"线程开始执行");
    Thread.yield();//线程礼让
    System.out.println(Thread.currentThread().getName()+"线程停止执行");
}

}

线程强制执行-join

  • join合并线程, 待此线程执行完成后, 在执行其他线程, 其他线程阻塞

  • 插队

package com.cyz.demo06;

public class TestJoin implements Runnable {

@Override
public void run() {
    for (int i = 0; i &lt; 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 &lt; 1000; i++){
        if( i == 200 ){
            thread.join();//插队
        }
        System.out.println("main"+i);
    }
}

}

观测线程状态

  1. NEW 尚未启动

  2. RUNNABLE 执行的线程

  3. BLOCKED 被阻塞等待监视器锁定的线程

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

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

  6. TERMINATED 已退出的线程

  • 注意

    • 退出的线程不能再重启

package com.cyz.demo07;

/**

  • com.cyz.demo07

  • @author cyz

  • @create 2020/05/29 10-06
    */
    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);//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);
     }
    

    }
    }

线程优先级

  • 范围1~10

  • Thread.MIN_PRIORITY = 1;

  • Thread.MAX_PRIORITY = 10;

  • Thread.NORM_PRIORITY = 5; 默认

  • 不一定高就先执行 只是概率高了

改变优先级

  • getPriority()

  • setPriority(int xxx)

package com.cyz.demo07;

public class TestPriority {

public static void main(String[] args) {
    System.out.println(Thread.currentThread().getName()+"----&gt;"+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);
    Thread t6 = new Thread(myPriority);

    //先设置优先级在启动
    t1.start();

    t2.setPriority(1);
    t2.start();

    t3.setPriority(4);
    t3.start();

    t4.setPriority(Thread.MAX_PRIORITY);
    t4.start();


    //范围报错

// t5.setPriority(-1);
// t5.start();
//
// t6.setPriority(11);
// t6.start();

}

}

class MyPriority implements Runnable {

@Override
public void run() {
    System.out.println(Thread.currentThread().getName() + "----&gt;" + Thread.currentThread().getPriority());
}

}

守护线程

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

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

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

  • 如: 后台记录操作日志, 监控内存, 垃圾回收

package com.cyz.demo07;

//上帝守护你
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();

    //虽然守护线程是while 但是虚拟机不管守护线程 所以当用户线程执行完毕后, 虚拟机停止了 就不在执行线程了
}

}

//上帝
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 &lt; 365000; i++){
        System.out.println("活着");
    }
    System.out.println("goodbye! world");
}

}

线程的同步

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

队列和锁

  • 保证线程安全

同步

  • synchronized

  • 当一个线程获取对象的排他锁, 独占资源, 其他线程必须等待, 使用后释放锁即可

存在问题

  1. 一个线程持有锁会导致其他所有需要此锁的线程挂起;

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

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

案例

package com.cyz.demo08;

//线程不安全的买票 有负数
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 ticketNums = 10;
//标位
boolean flag = true;//外部停止方式

@Override
public void run() {
    //买票
    while(flag){
        try {
            buy();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private void buy() throws InterruptedException {
    //判断是否有票
    if (ticketNums &lt;= 0){
        flag = false;
        return;
    }
    //模拟延时
    Thread.sleep(100);
    //买票
    System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}

}

package com.cyz.demo08;

//不安全的取钱
    //两个人去银行取钱  同一个账户
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100,"基金");

        Drawing you = new Drawing(account,50,"你");
        Drawing she = new Drawing(account,100,"她");

        you.start();
        she.start();
    }
}

//账户
class Account{
    int money;//余额
    String name;//卡名

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

//银行 模拟取款
class Drawing extends Thread{
    Account account;//账户
    //取了多少
    int drawingMoney;
    //现在手里有多少钱
    int nowMoeny;

    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;
        }


        //sleep放大问题的发生性
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //卡内余额 = 余额 - 你取的钱
        account.money = account.money - drawingMoney;
        //你手里的钱
        nowMoeny = nowMoeny + drawingMoney;

        System.out.println(account.name+"余额为:"+account.money);
        //Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName()+"手里的钱:"+nowMoeny);
    }
}
package com.cyz.demo08;

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

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

同步方法和同步块

  • synchronized

  • 缺陷: 影响效率

package com.cyz.demo08;

//线程不安全的买票 有负数
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 ticketNums = 10;
//标位
boolean flag = true;//外部停止方式

@Override
public void run() {
    //买票
    while(flag){
        try {
            buy();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//synchronized 同步  锁this
private synchronized void buy() throws InterruptedException {
    //判断是否有票
    if (ticketNums &lt;= 0){
        flag = false;
        return;
    }
    //模拟延时
    Thread.sleep(100);
    //买票
    System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}

}

package com.cyz.demo08;
 
//不安全的取钱
//两个人去银行取钱  同一个账户
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100, "基金");

        Drawing you = new Drawing(account, 50, "你");
        Drawing she = new Drawing(account, 100, "她");

        you.start();
        she.start();
    }
}

//账户
class Account {
    int money;//余额
    String name;//卡名

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

//银行 模拟取款
class Drawing extends Thread {
    Account account;//账户
    //取了多少
    int drawingMoney;
    //现在手里有多少钱
    int nowMoeny;

    public Drawing(Account account, int drawingMoney, String name) {
        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;
            }

            //sleep放大问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //卡内余额 = 余额 - 你取的钱
            account.money = account.money - drawingMoney;
            //你手里的钱
            nowMoeny = nowMoeny + drawingMoney;

            System.out.println(account.name + "余额为:" + account.money);
            //Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手里的钱:" + nowMoeny);
        }
    }
}
package com.cyz.demo08;

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

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

package com.cyz.demo08;

import java.util.concurrent.CopyOnWriteArrayList;

public class TestJOC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++){
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();

    }
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(list.size());
}

}

死锁

  • 某一个同步块同时拥有两个以上对象的锁时, 就可能会发生死锁 的问题

  • 自己有一个锁,但还需要另外一个对象的锁, 僵持 不愿意先给

  • 多个线程互相抱着对方需要的资源, 然后形成僵持

package com.cyz.demo09;

//死锁: 多个线程互相抱着对方需要的资源, 然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"你");
Makeup g2 = new Makeup(1,"她");

    g1.start();
    g2.start();
}

}

//口红
class Liostick{

}

//镜子
class Mirror{

}

class Makeup extends Thread{

//需要的资源只有一份,用static来保证只有一份
static Liostick liostick = new Liostick();
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) {
        e.printStackTrace();
    }
}

private void makeup() throws InterruptedException {
    if (choice==0){
        synchronized (liostick){//获得口红的锁
            System.out.println(this.girlName+"获得口红的锁");
            Thread.sleep(1000);

            synchronized (mirror){//1s后想获得镜子的锁
                System.out.println(this.girlName+"获得镜子的锁");
            }
        }
    }else{
        synchronized (mirror){//获得镜子的锁
            System.out.println(this.girlName+"获得镜子的锁");
            Thread.sleep(2000);

            synchronized (liostick){//2s后想获得口红的锁
                System.out.println(this.girlName+"获得口红的锁");
            }
        }
    }
}

}

  • 解决

	package com.cyz.demo09;

//死锁: 多个线程互相抱着对方需要的资源, 然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"你");
Makeup g2 = new Makeup(1,"她");

    g1.start();
    g2.start();
}

}

//口红
class Liostick{

}

//镜子
class Mirror{

}

class Makeup extends Thread{

//需要的资源只有一份,用static来保证只有一份
static Liostick liostick = new Liostick();
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) {
        e.printStackTrace();
    }
}

private void makeup() throws InterruptedException {
    if (choice==0){
        synchronized (liostick){//获得口红的锁
            System.out.println(this.girlName+"获得口红的锁");
            Thread.sleep(1000);
        }
        synchronized (mirror){//1s后想获得镜子的锁
            System.out.println(this.girlName+"获得镜子的锁");
        }
    }else{
        synchronized (mirror){//获得镜子的锁
            System.out.println(this.girlName+"获得镜子的锁");
            Thread.sleep(2000);
        }
        synchronized (liostick){//2s后想获得口红的锁
            System.out.println(this.girlName+"获得口红的锁");
        }
    }
}

}

产生死锁的必要条件

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

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

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

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

Lock锁

  • 显示定义同步锁

  • 每次只能有一个线程对Lock对象加锁

package com.cyz.demo09;

import java.util.concurrent.locks.ReentrantLock;

public class TsetLock {
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 ticketNums = 10;

//定义Lock锁
private final ReentrantLock lock = new ReentrantLock();

@Override
public void run() {
    while (true){

        try {
            lock.lock();//加锁

            if (ticketNums &gt; 0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(ticketNums--);
            }else{
                break;
            }

        } finally {
            //解锁
            lock.unlock();
        }
    }
}

}

Synchronized 与 Lock 的对比

  1. Lock是显示锁(手动开启和关闭锁) synchronized是隐式锁, 出现作用域自动释放

  2. Lock只有代码块锁, synchronized有代码块锁和方法锁

  3. 使用Lock锁, JVM 将花费较少的时间来角度线程, 性能更好, 并且具有更好的扩展性 (提供更多的子类)

  4. 优先使用顺序

    1. Lock

    2. 同步代码块(已经进入了方法体, 分配了相应资源)

    3. 同步方法(在方法体之外)

生产者和消费者

  • 仓库有东西, 通知消费者来拿, 仓库没有东西 通知生产者生成, 共享着同一个资源, 相互依赖条件

线程通信

方法名作用
wait() 表示线程一直等待, 直到其他线程通知, 与sleep不同, 会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程, 优先级别高的优先调度
  • 注意

均是Object类的方法, 都只能在同步方法或者同步代码块中使用, 
否则会抛出异常IllegalMonitorStateException

实现方式

方式一

  • 管程法

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

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

  • 缓冲区: 消费者不能直接使用生产者的数据, 他们之间有个缓冲区

  • 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

package com.cyz.demo10;
//管程法
    //生产者, 消费者, 产品, 缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
    new Productor(container).start();
    new Consumer(container).start();
}

}

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

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

@Override
public void run() {
    for (int i = 0; i &lt; 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 &lt; 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) {
            e.printStackTrace();
        }
    }

    //如果没有满, 我们就需要丢入产品
    chickens[count]=chicken;
    count++;

    //可以通知消费者消费了
    this.notifyAll();
}

//消费者消费产品
public synchronized Chicken pop(){
    //判断能否消费
    if(count==0){
        //等待生产者生产, 消费者等待
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //如果可以消费
    count--;
    Chicken chicken = chickens[count];

    //通知生产者生产
    this.notifyAll();

    return chicken;
}

}

方式二

  • 信号灯法 标记等待和通知

package com.cyz.demo10;

//信号灯法
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&lt; 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&lt; 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) {
            e.printStackTrace();
        }
    }
    System.out.println("演员表演了:"+voice);
    //通知观众看7
    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;
}

}

线程池

  • 避免频繁创建线程池

  • 提前创建好多个线程, 放入线程池, 使用时直接获取,使用完放回池中, 实现重复利用

好处

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

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

  3. 便于线程管理

    1. corePoolSize: 核心池的大小

    2. maximumPoolSize: 最大线程数

    3. keepAliveTime: 线程没有任务时最多保持多长时间后会停止

实现

  • ExecutorService

  • Executor

  1. execute() 执行Runnable

  2. shutdown() 关闭连接池

package com.cyz.demo10;
?
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());
  }
}
?

JAVA多线程

原文:https://www.cnblogs.com/liang-chen-fly/p/14490872.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!