线程基础

总结

1、进程包含多个线程,至少一个主线程
2、线程三种创建方式:
	继承Thread类
	实现Runnanble接口
	实现Callable接口  重写call方法   开启线程池,
	ExecutorService ser = Executors.newFixedThreadPool(3); 
	Future<Boolean> r1 = ser.submit(t1);
	r1.get()
        
3、Lambda表达式:函数式接口,只包含一个抽象方法
4、线程状态:
	新建
	就绪
	运行
	阻塞
	死亡
5、线程方法: 
	setPriority(int newPriority) :更改线程优先级       优先级1-10 默认5  优先级高只是调度概率高,不代表一定第一个调用
	static void sleep(Long mills) :在指定的毫秒数内让当前正在执行的线程休眠     
	void join() :插队让其他线程暂停,等待该线程终止  		其他线程阻塞,先执行本线程
	static void yield() :暂停当前正在执行的线程对象,并执行其他线程     线程暂停不阻塞,不一定礼让成功,看cpu调度
	void interrupt() : 中断线程	
	boolean isAlive() :测试线程是否处于活动状态
6、线程的停止不建议使用JDK的stop方法,建议设置一个flag,去停止代码的执行
7、守护线程:
	thread.setDaemon(true);//true为设置为守护线程,如果不设置为守护线程,则
      	thread.start();
	启动守护线程,再开启用户线程
	用户线程结束,守护线程也会结束
	应用:后台记录操作日志、监控内存
8、线程同步:
	同步解决方法:
		synchronized方法
		synchronized代码块
		lock锁 lock.lock(); lock.unlock();
9、死锁:对方都锁着自己需要的对象资源,解决办法就是 避免嵌套synchronized
10、线程协作:
	管程法:生产者、消费者、缓冲区
	信号灯法:立一个标志,如果为true,就让线程 等待,如果为false,就通知另一个人,将两个人衔接起来
	
	

线程

任务、进程、线程、多线程
进程是系统分配的
进程中包含若干个线程,至少包含一个主线程(main线程)

1、线程的创建

三种创建方式:

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口(了解)

Thread类

//创建线程方式一:集成Thread类,重写Run方法,调用start开启线程
//线程开启不一定立即执行,由cpu调度执行
public class ThreadTest extends Thread {
    @Override
    public void run() {
        super.run();
        //run方法线程体
        for (int i = 0; i <20 ; i++) {
            System.out.println("我在看代码"+i+"次");
        }
    }
    public static void main(String[] args) {
        //mian主线程

        //创建线程对象
        ThreadTest threadTest = new ThreadTest();
       // threadTest.run();   //run方法是先执行run再执行下面的代码
        threadTest.start();   //start方法是同时执行run方法和下面的for循环
        for (int i = 0; i <200 ; i++) {
            System.out.println("我在学习多线程"+i+"次");
        }
    }
}

实现多线程下载图片

//练习Thread,实现多线程同步下载图片
public class ThreadTest01 extends Thread{
    private String url;
    private String name;

    public ThreadTest01(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        super.run();
        WebLoader webLoader =new WebLoader();
        webLoader.downLoader(url,name);
        System.out.println("下载文件名:"+name);
    }

    public static void main(String[] args) {
        ThreadTest01 t1 =new ThreadTest01("https://t9.baidu.com/it/u=583874135,70653437&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=97e11c2abcd610a41c13b066b52a3779","a.jpg");
        ThreadTest01 t2 =new ThreadTest01("https://t9.baidu.com/it/u=1307125826,3433407105&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=90b5ea80ecf44290e31e3bb74580d7b5","b.jpg");
        ThreadTest01 t3 =new ThreadTest01("https://t9.baidu.com/it/u=1761131378,1355750940&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=452c83d05f07418798085d23a58b6628","c.jpg");
        t1.start();
        t2.start();
        t3.start();
    }
}
//下载器
class WebLoader{
    //下载方法
    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异常");
        }
    }
}

Runnable

//创建线程方法二:实现Runnable接口,重写run方法,创建线程对象,执行线程需要丢入runnable接口实现类,调用start方法
public class RunnableTest implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 200 ; i++) {
            System.out.println("我在学习"+i);
        }
    }

    public static void main(String[] args) {
        RunnableTest test = new RunnableTest();
        Thread thread =new Thread(test);
        thread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("我在看视频"+i);
        }
    }
}

多线程操作同一个对象

//多个线程操作同一个对象
//模拟抢火车票
public class ThreadTest2 implements Runnable {
    private int ticketNum=10;
    @Override
    public void run() {
        while (true) {
            try {
                if (ticketNum < 1) {
                    break;
                }
               Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "--->拿到了弟" + ticketNum-- + "张票");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        ThreadTest2 test2 = new ThreadTest2();
        new Thread(test2, "小明").start();
        new Thread(test2, "黄牛").start();
        new Thread(test2, "老师").start();
    }
}

模拟龟兔赛跑

//模拟龟兔赛跑
public class Run 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 b = gaveOver(i, Thread.currentThread().getName());
            if(b){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
        }
    }

    public boolean gaveOver(int i,String name){
        if(winner!=null){
            return true;
        }else {
            if(i==100){
                winner=name;
                System.out.println("winner is"+ name);
                return true;
            }
            return false;
        }
    }

    public static void main(String[] args) {
        Run run =new Run();
        new Thread(run,"兔子").start();
        new Thread(run,"乌龟").start();
    }
}

Callable

//Callable
public class CallableTest implements Callable<Boolean> {
    private String url;
    private String name;

    public CallableTest(String url, String name) {
        this.url = url;
        this.name = name;
    }
    @Override
    public Boolean call() throws Exception {
        WebLoader webLoader =new WebLoader();
        webLoader.downLoader(url,name);
        System.out.println("下载文件名:"+name);
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTest t1 =new CallableTest("https://t9.baidu.com/it/u=583874135,70653437&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=97e11c2abcd610a41c13b066b52a3779","a.jpg");
        CallableTest t2 =new CallableTest("https://t9.baidu.com/it/u=1307125826,3433407105&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=90b5ea80ecf44290e31e3bb74580d7b5","b.jpg");
        CallableTest t3 =new CallableTest("https://t9.baidu.com/it/u=1761131378,1355750940&fm=79&app=86&size=h300&n=0&g=4n&f=jpeg?sec=1601707164&t=452c83d05f07418798085d23a58b6628","c.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();
    }
}
//下载器
class WebLoader{
    //下载方法
    public  void downLoader(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常");
        }
    }
}

2、静态代理

//静态代理模式总结:
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处:
    //代理对象可以做很多真实对象做不了的事情
    //真实对象专注做自己的事情
public class StaticPoxy {
    public static void main(String[] args) {
        //线程就是静态代理模式,Thread作为代理人同样实现了Runnable接口,代理Runnable的真实实现对象去完成任务
        //new Thread(Runnable的实现类).start();
        new Poxy(new You()).happyMarry(); //
    }
}
interface Marry{
    void happyMarry();
}
class You implements  Marry{
    @Override
    public void happyMarry() {
        System.out.println("xxx要结婚了");
    }
}
class Poxy implements Marry{
    private Marry marry;
    public Poxy(Marry marry) {
        this.marry = marry;
    }
    @Override
    public void happyMarry() {
        before();
        marry.happyMarry();
        after();
    }
    private void after() {
        System.out.println("收尾款");
    }
    private void before() {
        System.out.println("布置现场");
    }
}

3、Lambda表达式

理解函数式接口是学习Lambda表达式的关键所在
函数式接口的定义:

  • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
  • 对于函数式接口,我们可以通过lambda表达式来创建该接口对象
//推到lambda表达式,接口方法无参数
public class LambdaTest {

    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("i like chenmeng2");
        }
    }
    public static void main(String[] args) {
        //1、正常类
        ILike like =new Like();
        like.lambda();
        //2、静态内部类
        like =new Like2();
        like.lambda();
        //3、局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i like chenmeng3");
            }
        }
        like= new Like3();
        like.lambda();
        //4、匿名内部类
        like =new ILike(){
            @Override
            public void lambda() {
                System.out.println("i like chenmeng4");
            }
        };
        like.lambda();
        //5、lambda表达式
        like = ()->{System.out.println("i like chenmeng5");};
        like.lambda();
    }

}
//定义一个函数式接口
interface ILike{
    void lambda();
}

class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("i like chenmeng1");
    }
}
//推到lambda表达式,接口方法含参数,多个参数亦可
public class LambdaTest2 {
    static class Love1 implements ILove {
        @Override
        public void love(int a) {
            System.out.println("i love chenmeng 1");
        }
    }
    public static void main(String[] args) {
        //1、正常类
        ILove love = new Love();
        love.love(2);
        //2、静态内部类
        love = new Love1();
        love.love(2);
        //3、局部内部类
        class Love2 implements ILove {
            @Override
            public void love(int a) {
                System.out.println("i love chenmeng 2");
            }
        }
        love = new Love2();
        love.love(2);
        //4、匿名内部类
        love = new ILove() {
            @Override
            public void love(int a) {
                System.out.println("i love chenmeng 3");
            }
        };
        love.love(2);
        //5、Lambda表达式
        love = (int a) -> {
            System.out.println("i love chenmeng 4");
        };
        //6、Lambda表达式,简化去掉返回值
        love.love(2);
        love = (a) -> {
            System.out.println("i love chenmeng 5");
        };
        love.love(2);
        //7、Lambda表达式,简化去掉返回值和括号
        love = a -> {
            System.out.println("i love chenmeng 6");
        };
        love.love(2);
        //8、Lambda表达式,去掉大括号
        love=a->System.out.println("i love chenmeng 7");
        love.love(2);
        //总结:
        // lambda表达式只有是一行代码的情况下才能写成一行,如果有多行就用代码块包裹,不能省区大括号
        //必须是函数式接口,即接口内只有一个抽象方法
    }

}
interface ILove {
    void love(int a);
}
class Love implements ILove {
    @Override
    public void love(int a) {
        System.out.println("i love chenmeng ");
    }
}

4、线程状态

五大状态
在这里插入图片描述
线程方法:

  • setPriority(int newPriority) :更改线程优先级
  • static void sleep(Long mills) :在指定的毫秒数内让当前正在执行的线程休眠
  • void join() :插队让其他线程暂停,等待该线程终止
  • static void yield() :暂停当前正在执行的线程对象,并执行其他线程
  • void interrupt() : 中断线程
  • boolean isAlive() :测试线程是否处于活动状态

线程停止

  • 不推荐使用JDK提供的stop、destory方法;
  • 推荐线程自己停止下来;
  • 建议建立一个标志位进行终止变量,当flag=false时,则线程终止运行;
//测试stop
    //1、建议线程正常停止,利用次数,不建议死循环
    //2、建议使用标志位,设置一个标志位
    //3、不要使用stop、destory等过时的方法
public class ThreadStop implements Runnable {

   //设置一个标志位
    private static Boolean flag = true;
    @Override
    public void run() {
        while(flag){
            System.out.println("run--->Thread");
        }
    }
    public static void main(String[] args) {
        ThreadStop threadStop =new ThreadStop();
        new Thread(threadStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main:====>"+i);
            if(i==900){
                threadStop.stop();
                System.out.println("第"+i+"次,线程停止了");
                break;
            }
        }
    }
    public void stop(){
        flag=false;
    }
}

线程休眠

  • sleep(时间) 指定当前线程阻塞的毫秒数;
  • sleep存在异常InterruptedException
  • sleep时间达到后,线程进入就绪状态
  • sleep可以模拟网络延时,倒计时等
  • 每个对象都有一把锁,sleep不会释放锁
//模拟倒计时
public class SleepTest {

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

    public static void dateTime() throws InterruptedException {

        Date  startTime = new Date(System.currentTimeMillis());
        while(true){
            Thread.sleep(1000);
            String time = new SimpleDateFormat("hh:mm:ss").format(startTime);
            startTime = new Date(System.currentTimeMillis());//更新系统时间
            System.out.println(time);
        }
    }
    public static void main(String[] args) {
        try {
            dateTime();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让cpu重新调度,礼让不一定成功!看cpu心情
//测试礼让线程
    //礼让不一定成功,看cpu心情
public class YieldTest {
    public static void main(String[] args) {
        MyYield myYield =new MyYield();
        new Thread(myYield,"小明").start();
        new Thread(myYield,"小暗").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合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
  • 理解为强制执行插队
//模拟join,线程强行插队
public class JoinTest 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 {
   		 //启动我们的线程
        JoinTest joinTest =new JoinTest();
        Thread thread =new Thread(joinTest);
        thread.start();
        //主线程
        for (int i = 0; i < 200; i++) {
            System.out.println("我是main线程,我来了"+i+"次");
            if(i==100){
                thread.join();
            }
        }
    }
}

监测线程状态

//测试监测线程状态
public class StatusTest {

    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("*************");
        });
        //1、NEW
        Thread.State state = thread.getState();
        System.out.println(state);
        //2、RUNNABLE
        thread.start();
        state=thread.getState();
        System.out.println("***"+state);
        while(state!= Thread.State.TERMINATED){
            //3、TIMED_WAITING
            Thread.sleep(100);
            state=thread.getState();
            System.out.println(state);
        }
        //4、TERMINATED
        state=thread.getState();
        System.out.println(state);
        
		thread.start();//重新启动线程会报错,线程结束之后就会死亡
    }
}

5、线程优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定 应该调度哪个线程来执行
  • 线程的优先级用数字来表示,范围从1~10:min 1 max 10 norm 5
  • 使用以下方式改变或获取优先级。getPriority();setPriority(int 5)
  • 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看cpu的调度
  • 默认优先级为5
//测试线程的优先级
public class PriorityTest {
    public static void main(String[] args) {
        System.out.println("主线程");
        MyThread myThread =new MyThread();
        Thread thread1 = new Thread(myThread,"a");
        Thread thread2 = new Thread(myThread,"b");
        Thread thread3 = new Thread(myThread,"c");
        Thread thread4 = new Thread(myThread,"d");
        //先设置优先级,再启动线程
        thread1.setPriority(Thread.MIN_PRIORITY);
        thread1.start();
        thread2.setPriority(Thread.NORM_PRIORITY);
        thread2.start();
        thread3.start();
        thread4.setPriority(Thread.MAX_PRIORITY);
        thread4.start();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"的优先级:"+Thread.currentThread().getPriority());
    }
}

6、守护线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如:后台记录操作日志,监控内存,垃圾回收等等
//测试守护线程
//用户线程结束之后,守护线程也会结束,不过结束回收机制需要时间
public class TestDaemon {
    public static void main(String[] args) {
        You you = new You();
        God god = new God();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//true为设置为守护线程,如果不设置为守护线程,则
        thread.start();

        new Thread(you).start();
    }
}
class You implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("***********开心的活着*************");
        }
        System.out.println("*********goodBye***********");
    }
}
class God implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("******上帝保佑你********");
        }
    }
}

7、线程同步

  • 处理多线程问题时,多个线程同时访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步。线程同步就是一种等待机制,多个需要同时访问该对象的线程进入这个对象的等待池,形成队列,等待前面的线程使用完毕,下个线程再使用;
  • 队列和锁:防止多个线程访问冲突,除了队列,还需要引入锁机制(synchronized),确保线程的安全,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可;
  • 存在以下问题1、锁 会导致其他线程挂起 2、加锁、释放锁会导致上下文切换,调度延时,引起性能问题 3、如果优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题;
//不安全的买票
public class ByTicket {
    public static void main(String[] args) {
        Ticket ticket =new Ticket();
        new Thread(ticket,"a").start();
        new Thread(ticket,"b").start();
        new Thread(ticket,"c").start();
    }
}
class Ticket implements Runnable {
    private int ticket = 10;
    Boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            try {
                Thread.sleep(1000);//延时可以放大问题所在
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            buy();
        }
    }
    public void buy() {
        if (ticket <= 0) {
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName() + "买到了第" + ticket-- + "张票");
    }
}
//不安全的取钱
public class UnSafeBank {
    public static void main(String[] args) {
        Account account=new Account("基金",100);
        Bank you = new Bank(account,50,"you");
        Bank girl = new Bank(account,100,"girl");
        you.start();
        girl.start();
    }
}

class Account {
    String name;
    int money;
    public Account(String name, int money) {
        this.name = name;
        this.money = money;
    }
}
class Bank extends Thread {
    Account account;
    int drawingMoney;
    int nowMoney;

    public Bank(Account account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        super.run();
        if (account.money - drawingMoney < 0) {
            System.out.println(this.getName() + "账户余额不足");
            return;
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //多线程同时操作一个对象时,实际上时两个线程对同一个内存区中的对象,属性的值,进行操作
        /*
        当延时时间较长,两个线程在此以下代码之前相遇,则会出现下列情况
        因此如果你娶了50之后,girl再取100时,此时accout.money实际上已经是50了,再取100,则
        account.money为-50,所以两个线程最后显示的剩余的钱都是-50
         */
        account.money = account.money - drawingMoney;
        nowMoney = nowMoney + drawingMoney;
        System.out.println(account.name+"剩余的钱是:"+account.money);
        System.out.println(this.getName()+"现在的钱是:"+nowMoney);
    }
}
//线程不安全集合
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());
        //9996 两个线程同时添加到list中的同一个位置,造成覆盖,
        //才会造成list.size小于10000
    }
}

同步解决方法

  • synchronized方法
 //synchronized同步方法,锁的this
   public synchronized void buy() {}
  • synchronized代码块
synchronized(obj){
}

需要修改内容的部分才需要锁,查询部分不需要锁,锁的太多,浪费资源

//安全的买票 只需在买票的方法上加synchronized修饰符
public class ByTicket {
    public static void main(String[] args) {
        Ticket ticket =new Ticket();
        new Thread(ticket,"a").start();
        new Thread(ticket,"b").start();
        new Thread(ticket,"c").start();
    }
}
class Ticket implements Runnable {
    private int ticket = 10;
    Boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            try {
                Thread.sleep(1000);//延时可以放大问题所在
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            buy();
        }
    }
    //synchronized同步方法,锁的this
    public synchronized void buy() {
        if (ticket <= 0) {
            flag = false;
            return;
        }
        System.out.println(Thread.currentThread().getName() + "买到了第" + ticket-- + "张票");
    }
}
//安全的取钱,将操作账户对象放到代码块中
public class UnSafeBank {
    public static void main(String[] args) {
        Account account=new Account("基金",10000);
        Bank you = new Bank(account,50,"you");
        Bank girl = new Bank(account,100,"girl");
        you.start();
        girl.start();
    }
}

class Account {
    String name;
    int money;
    public Account(String name, int money) {
        this.name = name;
        this.money = money;
    }
}
class Bank extends Thread {
    Account account;
    int drawingMoney;
    int nowMoney;

    public Bank(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(this.getName() + "账户余额不足");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //多线程同时操作一个对象时,实际上时两个线程对同一个内存区中的对象,属性的值,进行操作
        /*
        当延时时间较长,两个线程在此以下代码之前相遇,则会出现下列情况
        因此如果你娶了50之后,girl再取100时,此时accout.money实际上已经是50了,再取100,则
        account.money为-50,所以两个线程最后显示的剩余的钱都是-50
         */
            account.money = account.money - drawingMoney;
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.name+"剩余的钱是:"+account.money);
            System.out.println(this.getName()+"现在的钱是:"+nowMoney);
        }
    }
}
线程不安全集合变安全
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();
        }
        //主线程不加sleep,主线程会提前跑完,
        //则list.size的值可能不是10000,因为其他线程还没有执行结束
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
        //9996 两个线程同时添加到list中的同一个位置,造成覆盖,才会造成list.size小于10000
    }
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) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

8、死锁

//死锁:多个线程抱着对方需要的资源,然后形成僵持
public class DeadLock {
    public static void main(String[] args) {
        MakeUp a =new MakeUp(0,"a");
        MakeUp b =new MakeUp(1,"b");
        a.start();
        b.start();
    }
}
class LipStick {
}
class Mirror {
}
class MakeUp extends Thread {
    static LipStick lipStick=new LipStick();
    static Mirror mirror=new Mirror();

    int choice;
    String girlName;

    public MakeUp(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void makeUp() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipStick) {
                Thread.sleep(1000);
                System.out.println(this.girlName + "获得口红的锁");
                synchronized (mirror) {
                    System.out.println(this.girlName + "获得镜子的锁");
                }
            }
        } else {
            synchronized (mirror) {
                Thread.sleep(2000);
                System.out.println(this.girlName + "获得镜子的锁");
                synchronized (lipStick) {
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
        }
    }
}
//解决死锁只需要将synchronized代码块提出
public void makeUp() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipStick) {
                Thread.sleep(1000);
                System.out.println(this.girlName + "获得口红的锁");
            }
            synchronized (mirror) {
                System.out.println(this.girlName + "获得镜子的锁");
            }
        } else {
            synchronized (mirror) {
                Thread.sleep(2000);
                System.out.println(this.girlName + "获得镜子的锁");
            }
            synchronized (lipStick) {
                System.out.println(this.girlName + "获得口红的锁");
            }
        }
    }

9、Lock锁

  • 从JDK5.0开始,Java提供了更强大的线程同步机制——通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当
  • java.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象枷锁,线程开始访问共享资源之前应先获得Lock对象
  • ReentrantLock类实现了lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentransLock,可以显示加锁,释放锁。
//测试Lock锁
public class LockTest {
    public static void main(String[] args) {
        Test test = new Test();
        new Thread(test, "a").start();
        new Thread(test, "b").start();
        new Thread(test, "c").start();
    }
}
class Test implements Runnable {
    int ticketNum = 10;
    ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (ticketNum > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "买到了第" + ticketNum-- + "张票");
                } else {
                    break;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}

synchronized和Lock的对比:

  • Lock是显式锁,(手动开启和关闭锁,别忘记关锁);synchronized是隐式锁,出了作用域自动释放。
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,jvm将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

10、线程协作-生产者消费者模式

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

  • 对于生产者,没有生产产品之前,要通知消费者等待,生产产品之后,要通知 消费者消费

  • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新产品以供消费

  • 生产者消费者问题中,仅有synchronized是不够的:1、synchronized可阻止并发更新同一个共享资源,实现了同步 2、synchronized 不能用来实现不同线程之间的消息传递
    解决方式一:管程法

  • 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)

  • 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)

  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个缓冲区
    生产者将生产好的数据放入缓冲区,消费者从缓冲区拿数据
    在这里插入图片描述
    解决方式二:信号灯法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值