多线程(下篇)

多线程

线程状态

状态介绍

  1. new

    ​ 创建进入新生状态

  2. 就绪状态

    使用start方法立刻进入就绪状态,但是不一定会立刻执行

  3. 运行状态

    CPU调度运行时进入此状态

  4. 阻塞状态

    当调用sleep,wait或者同步锁订时。线程进入阻塞状态

  5. dead

    运行结束

状态图

线程停止

1、线程自动停止

2、利用标识符停止

实例

package com.py.demo01;

public class TestStop implements Runnable {

    private boolean flag = true;
    int i = 0;
    @Override
    public void run() {
       //标志位
        while (flag){
            System.out.println("线程"+(i++));
        }
    }

    public void stop(){
        this.flag= false;
    }

}
class Test{

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i < 800; i++) {
            if (i==200){
                testStop.stop();
                System.out.println("=======线程结束======");
            }
            System.out.println("主线程"+i);
        }
    }
}



线程休眠

特点

sleep(t)t为休眠时间

线程休眠结束后,进入就绪状态

sleep会抛出InterruptedException异常

sleep可以模拟网络延时、倒计时等

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

源码

package com.py.demo01;

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

public class TestSleep {

    public static void main(String[] args) {

        //倒计时执行
        try {
            countdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //获取系统当前时间
        Date time = new Date(System.currentTimeMillis());
        while (true){

            System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));
            try {
                Thread.sleep(1000);
                //刷新系统时间
                time = new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }




//
    }

    //倒计时
    public static void countdown() throws InterruptedException {
        int time = 10;
        while (time!=0){
            System.out.println("倒计时"+ time);
            time--;
      Thread.sleep(1000);
        }

    }
}

线程礼让(yield)

礼让不一定成功,具体情况看CPU调度

demo

package com.py.demo01;

public class TestYield {
    public static void main(String[] args) {
        
        YeildDemo yeildDemo = new YeildDemo();
        
        new Thread(yeildDemo,"a").start();
        new Thread(yeildDemo,"b").start();
    }
}
//线程
class YeildDemo implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始====>");
        //如果线程是a,此时将线程礼让出来
        if (Thread.currentThread().getName()=="a") {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"线程停止====>");
    }
}

线程加入(Join)

当此线程结束后才可以执行其他线程,此时其他线程阻塞(可以理解为插队)

demo:让大佬插主线程的队

package com.py.demo01;

public class TestJoin {

    public static void main(String[] args) {

        JoinTread thread = new JoinTread();
        //线程进入就绪态
        Thread join = new Thread(thread);
        join.start();


        for (int i = 0; i < 300; i++) {
            if (i==100){
                try {
                    //线程开始插队
                    join.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("我是主线程"+ "	"+ i);
        }

    }



}
class JoinTread implements Runnable{

    @Override
    public void run() {
        //休眠的目的是让主线程可以跑到此线程的前面去
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 200; i++) {
            System.out.println("大佬来了"+"	"+i);
        }
    }
}



线程的状态(State)

常量

  • NEW
    尚未启动的线程处于此状态。
  • RUNNABLE
    在Java虚拟机中执行的线程处于此状态。
  • BLOCKED
    被阻塞等待监视器锁定的线程处于此状态。
  • WAITING
    正在等待另一个线程执行特定动作的线程处于此状态。
  • TIMED_WAITING
    正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
  • TERMINATED
    已退出的线程处于此状态。

Demo:使用主线程查看a线程状态

package com.py.demo01;

public class TestState implements Runnable {

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

    public static void main(String[] args) {
        TestState people = new TestState();

        //new状态
        Thread thread = new Thread(people,"a");
        Thread.State state = thread.getState();
        System.out.println(state);

        //就绪状态
        thread.start();
        state = thread.getState();
        System.out.println(state);

        while (state != Thread.State.TERMINATED){
            //打印当前状态
            try {
                //0.1秒查看状态一次
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = thread.getState();
            System.out.println(state);

        }
        System.out.println("/////////////////////////");

    }
}

线程优先级(Priority)

java提供线程调度器,当前所有准备状态的线程。线程调度器根据线程的优先级来决定线程的执行顺序。

线程的优先度作为参考,但是实际情况要依据CPU调度进行处理

Demo:优先级程度高的先执行

package com.py.demo2;

public class TestPriority implements Runnable {

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

    public static void main(String[] args) {
        TestPriority testPriority = new TestPriority();

        Thread td01 = new Thread(testPriority);
        Thread td02 = new Thread(testPriority);
        Thread td03 = new Thread(testPriority);
        Thread td04 = new Thread(testPriority);
        Thread td05 = new Thread(testPriority);


        td01.setPriority(9);
        td01.start();


        td02.setPriority(4);
        td02.start();


        td03.setPriority(5);
        td03.start();


        td04.setPriority(7);
        td04.start();


        td05.setPriority(2);
        td05.start();
    }

}

守护线程(daemon)

虚拟机会守护用户线程但是不会保证守护者线程完整执行

当用户线程执行结束后,守护线程会被虚拟机停止

Demo:自动结束的守护者线程

package com.py.demo2;

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        People people = new People();

        //设置God线程为守护者线程
        Thread thread = new Thread(god);
        thread.setDaemon(true);
        thread.start();

        //启动用户线程
        new Thread(people).start();


    }
}
//守护者线程
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("God will protecting you");
        }
    }
}
//用户线程
class People implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(" a wandfull year!!!!");
        }

        System.out.println("======BYE WORLD======");
    }
}

线程池

类似于连接池,在线程池中创建线程,在有线程任务需要执行时,由线程池出线程,在线程任务结束之后将线程归还给线程池。

线程池使用步骤:

1、执行人 Executor类中提供的静态方法newFixedThreadPool(int nThreads),创建一个拥有几个线程的线程池。

2、创建一个类,重写run() 方法,创建线程任务

3、调用ExecutorService中的submit()方法,提交任务线程

4、结束线程池使用ExecutorService 的shutdown()方法【不建议使用】

package com.py.demo04;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 1、执行人 Executor类中提供的静态方法newFixedThreadPool(int nThreads),创建一个拥有几个线程的线程池。
 * 2、创建一个类,重写run() 方法,创建线程任务
 * 3、调用ExecutorService中的submit()方法,提交任务线程
 */

public class DemoThreadpool {
    public static void main(String[] args) {

//1、执行人 Executor类中提供的静态方法newFixedThreadPool(int nThreads),创建一个拥有几个线程的线程池。
        ExecutorService executorService = Executors.newFixedThreadPool(2);
//     * 2、创建一个类,重写run() 方法,创建线程任务
        RunTasker tasker = new RunTasker();
//     3、调用ExecutorService中的submit()方法,提交任务线程
        executorService.submit(tasker);
        executorService.submit(tasker);
        executorService.submit(tasker);
    }

}

线程锁

线程同步机制

并发

多个线程执行同一个资源,并发指的是多个任务交替进行,

同步

线程同步实际上就是一种等待机制,多个同时需要访问此对象的线程进入对象的等待池形成队列

知识延展

同步VS异步

同步和异步通常用来形容一次方法调用。同步方法调用开始后,调用者必须等待被调用的方法结束后,调用者后面的代码才能执行。而异步调用,指的是,调用者不用管被调用方法是否完成,都会继续执行后面的代码,当被调用的方法完成后会通知调用者。

并发与并行

并发和并行是十分容易混淆的概念。并发指的是多个任务交替进行,而并行则是指真正意义上的“同时进行”。实际上,如果系统内只有一个CPU,使用多线程时,在真实系统环境下不能并行,只能通过切换时间片的方式交替进行,从而并发执行任务。真正的并行只能出现在拥有多个CPU的系统中。

阻塞和非阻塞

阻塞和非阻塞通常用来形容多线程间的相互影响,比如一个线程占有了临界区资源,那么其他线程需要这个资源就必须进行等待该资源的释放,会导致等待的线程挂起,这种情况就是阻塞,而非阻塞就恰好相反,它强调没有一个线程可以阻塞其他线程,所有的线程都会尝试地往前运行。

临界区

临界区用来表示公共资源或者说是共享数据,可以被多个线程使用。但是每个线程使用时,一旦临界区资源被一个线程占有,那么其他线程必须等待。

并发问题出现

Demo01:经典买票问题

package com.py.demo2;

public class BuyTicket {
    public static void main(String[] args) {
        Buy station = new Buy();
        //三人分别买票
        new Thread(station,"张三").start();
        new Thread(station,"李四").start();
        new Thread(station,"王五").start();
    }

}

class Buy implements Runnable{
    //票数
    private int sum = 10;
    boolean flag = true;
    @Override

    public void run() {
//        停止标志位
        while (flag){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获得票的人
            System.out.println(Thread.currentThread().getName()+ "得到了第"+ sum--+"张票");
            //如果票没了就停止循环
            if (sum<=0){
                flag = false;
            }
        }
    }
}

输出截图

image-20200921104500331

Demo02银行取钱问题

package com.py.demo03;

public class DrawingMoney {
    public static void main(String[] args) {
        //新建银行
        Bank bank = new Bank(10000, "账户");
        //取款操作01
        Drawing lisi = new Drawing(bank,40,"李四");
        lisi.start();

        //取款操作02
        Drawing wangwu = new Drawing(bank, 4000, "王五");
        wangwu.start();

    }
}
class Bank{
    //银行账户
      int account ;
      //操作人姓名
      String name;

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

}

//取钱线程
class Drawing extends Thread {
    //银行
    private Bank bank;
    //要取得的钱数
    private int draw;
    //取钱的人
    private String name;

    public Drawing(Bank bank, int draw, String name) {
        super(name);
        this.bank = bank;
        this.draw = draw;
    }

    @Override
    public void run() {


            if (bank.account - draw < 0){
                System.out.println("账户余额不足,请重新设置取款金额");
                return;
            }
            bank.account = bank.account - draw;
            System.out.println("取款人"+Thread.currentThread().getName()+"	余额"+bank.account);

    }
}

输出截图

image-20200921155038664

造成了李四读到了王五提交后的数据

Synchronize

理论点

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

  • 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  • 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  • 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  • 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

修饰一个代码块

用static可以保证所需要的资源只有一份

package com.py.demo03;

/**
 * 同步线程
 */
class SyncThread implements Runnable {
    private static int count;

    public SyncThread() {
        count = 0;
    }

    public void run() {
        synchronized(this) {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":" + (count++));
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public int getCount() {
        return count;
    }
}

class Man{
    public static void main(String[] args) {
        
		/*       
		//案例1、锁定对象SyncThread后,两个线程开始排队,形成互斥
		 SyncThread syncThread = new SyncThread();
         Thread thread1 = new Thread(syncThread, "SyncThread1");
         Thread thread2 = new Thread(syncThread, "SyncThread2");
         thread1.start();
         thread2.start();*/
        
		//案例2、锁定对象SyncThread后,两个线程互不干扰,但是因为static存在,所以还是使用的一份count
        Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
         Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
         thread1.start();
         thread2.start();
    }
}

案例一

输出:

SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9

总结:

两个线程开始相互排队去执行run();一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。

案例二

输出:

SyncThread1:0
SyncThread2:1
SyncThread2:2
SyncThread1:2
SyncThread1:4
SyncThread2:3
SyncThread1:5
SyncThread2:6
SyncThread2:8
SyncThread1:7

总结

这时创建了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);我们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。

修饰一个方法

同步方法也会把同步代码锁住,只让一个程序执行。锁的对象是类对象,也就是this。

package com.py.demo03;

/**
 * 同步线程
 */
class SyncThread implements Runnable {
    private static int count;

    public SyncThread() {
        count = 0;
    }

    public synchronized void run() {
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println(Thread.currentThread().getName() + ":" + (count++));
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public int getCount() {
        return count;
    }
}

class Man{
    public static void main(String[] args) {
        Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
         Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
         thread1.start();
         thread2.start();
    }
}

死锁

多个线程同时抱有对方的需要的资源,导致程序阻塞。

package com.py.demo03;

public class TestLock {
    public static void main(String[] args) {
        Makeup thread01 = new Makeup(1,"李四");
        Makeup thread02 = new Makeup(0,"张三");

        thread01.start();
        thread02.start();
    }

}
class Pen{


}

class Peper{

}
class  Makeup extends Thread{
    //只有一份的笔 和  纸
    static Pen pen = new Pen();
    static Peper peper = new Peper();

    int choise;
    String user;

    public  Makeup(int choise,String user){
        this.user = user;
        this.choise = choise;
    }

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

    public void use() throws InterruptedException {
        if (choise == 0 ){
            //第一个人先进来获得到笔的使用权
            synchronized (pen){
                System.out.println(this.user + "获得到笔的使用权");
                Thread.sleep(1000);

                synchronized (peper){
                    System.out.println(this.user + "获得到纸的使用权");
                }
            }

        }else{
            synchronized (peper){
                System.out.println(this.user + "获得到纸的使用权");

                synchronized (pen){
                    System.out.println(this.user + "获得到笔的使用权");
                }
            }
        }
    }

}
package com.py.demo03;

public class TestLock {
    public static void main(String[] args) {
        Makeup thread01 = new Makeup(1,"李四");
        Makeup thread02 = new Makeup(0,"张三");

        thread01.start();
        thread02.start();
    }

}
class Pen{


}

class Peper{

}
class  Makeup extends Thread{
    //只有一份的笔 和  纸
    static Pen pen = new Pen();
    static Peper peper = new Peper();

    int choise;
    String user;

    public  Makeup(int choise,String user){
        this.user = user;
        this.choise = choise;
    }

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

    public void use() throws InterruptedException {
        if (choise == 0 ){
            //第一个人先进来获得到笔的使用权
            synchronized (pen){
                System.out.println(this.user + "获得到笔的使用权");
                Thread.sleep(1000);

                synchronized (peper){
                    System.out.println(this.user + "获得到纸的使用权");
                }
            }

        }else{
            synchronized (peper){
                System.out.println(this.user + "获得到纸的使用权");

                synchronized (pen){
                    System.out.println(this.user + "获得到笔的使用权");
                }
            }
        }
    }

}

程序卡死

image-20200923095846688

lock锁

与Synchronize相同,

不同之处,Lock是显锁(手动开启和关闭锁)synchronize是隐式锁,出了作用域自动释放

lock只有代码块锁,synchronized有代码块和方法锁。

lock锁性能更好,并且具有更好的扩展性

上锁与解锁

void lock(); 上锁

void unlock; 解锁

package com.py.demo03;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 同步线程
 */
class SyncThread implements Runnable {
    private static int count;

    public SyncThread() {
        count = 0;
    }
    //线程锁对象
     Lock lock = new ReentrantLock();

    public  void run() {

        for (int i = 0; i < 5; i++) {
            lock.lock();
            try {
                Thread.sleep(500);
                System.out.println(Thread.currentThread().getName() + ":" + (count++));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //解锁
                lock.unlock();
            }
        }
    }

    public int getCount() {
        return count;
    }
}

class Man{
    public static void main(String[] args) {
         SyncThread syncThread = new SyncThread();
         Thread thread1 = new Thread(syncThread, "SyncThread1");
         Thread thread2 = new Thread(syncThread, "SyncThread2");
         thread1.start();
         thread2.start();

    }
}

唤醒线程

线程休眠之后,由其他线程唤醒。

带参数的休眠方法:wait(time);时间达到之后自动唤醒

全部唤醒:notifyAll();

唤醒的是第一个进入锁对象 的线程,

休眠的是第一个进入锁对象 的线程,

package com.py.demo03;

/**
 * 等待唤醒案例
 * 顾客告知老板需要的食品的数量和种类,唤醒老板线程然后自己休息
 * 老板开始生产,生产完成后唤醒顾客线程
 *  老板和顾客只能一个在执行
 *  只有锁对象才能waiter notify
 */
public class DemoWait extends Thread{



    public static void main(String[] args) {
        //创建唯一锁
        Object obj = new Object();
        //顾客线程
        new Thread(){
            @Override
            public void run() {
                super.run();
                synchronized (obj){
                    //顾客告知老板需要的食品的数量和种类
                    System.out.println("要吃什么样的,要多少");
                    try {
                        //然后自己休息
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("吃完了走人");
                }
            }
        }.start();

        //老板线程
        new Thread(){
            @Override
            public void run() {
                //老板等待顾客叫完东西
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                super.run();
                synchronized (obj){
                    //老板开始生产,
                    System.out.println("老板开始制作");
                    //生产完成后唤醒顾客线程
                    obj.notify();
                }
            }
        }.start();
    }


}

线程通信

理论:

多线程并发执行时,在默认情况下cpu随机切换线程,当我们需要一个的多线程共同完成一件任务时,希望他们可以有顺序的执行,于是需要他们线程之间进行通信。

原文地址:https://www.cnblogs.com/yuknight/p/13740646.html