多线程知识总结

多线程知识总结

目录:
1.概念
2.进程和线程的区别
3.实现代码
4.模拟卖火车票实现多线程资源的抢夺
5.线程的2种实现方式
6.线程的生命周期
7.线程的状态
8.线程安全
9.死锁
10.线程通信
11.notify

                                     **正文**

1.概念:线程是进程中的一个个的任务和功能模块的任务执行路径,如同qq中的聊天发送信息和发送图片,扫描二维码都是线程。

2.线程和进程:
2.1 线程:进程由线程来实现功能的完成,进程可以是并发的,可以并发执行;
进程中至少有一个线程,线程中最先被执行的是main线程;
单核计算机只有一个线程;
进程中的线程资源共享;
2.2 进程:进程间的资源不共享,
进程可以并发进行;比如:可以同时用英雄联盟打游戏+用网易云听音乐,这2者间资源不共享,但是可以同时进行。
一个进程中至少有一个存活的线程,否则进程结束。

3.线程的2种实现方式:
1》继承thread

  public class Test1 {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        
        for(int i=0;i<10;i++){
            Thread.sleep(500);
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

//线程是用来完成某些任务的,那么任务的主体部分就在run 方法内部。
// 缺点:不能再继承其他的类,多个线程访问同一个数据的时候,处理相对复杂。要么单例,要么持有唯一对象的引用。
class MyThread extends Thread{
    public void run() {
        for(int i=0;i<10;i++){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
}

2》实现runnable接口
建议这种方式,因为java是单继承,多实现,所以尽量实现接口,少用继承

  public class Test1 {

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();

        //先创建Runnable 的实例
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target);
        thread.start();
}

class MyRunnable implements Runnable{
    //线程的执行的主体
    public void run() {
        for(int i=0;i<10;i++){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
  

4.线程的实现原理
cpu是如何处理这么多进程的任务的请求呢?
cpu 将执行的过程细分了非常微小的时间片。然后这些时间片会分配给不同的进程执行。
因为时间片非常短,所以宏观的感受就是所有的进程都是一起执行的。但是微观上来说,所有的进程都是线性执行的。
线程是cpu调度执行的最小单位,进程是cpu分配资源的最小单位。
cpu同时执行多个线程和进程,分配内存资源和实现任务调度。

4.模拟卖火车票实现多线程资源的抢夺

public class Test1 {
    public static void main(String[] args) {
        TicketThread tr = new TicketThread();
        Thread t = new Thread(tr);
        t.setName("窗口一");//3个窗口同时卖票可以共享100张票
        Thread t2 = new Thread(tr);
        t2.setName("窗口二");
        Thread t3 = new Thread(tr);
        t3.setName("窗口三");

        t.start();
        t2.start();
        t3.start();
    }
    
   static class TicketThread implements Runnable {
        int ticket = 100;
        Object obj = new Object();

        @Override
        public void run() {
            //method(); //method是实现了同步方法,可以解决火车票出现的问题,
            while (true) {
                synchronized (obj) {  //加锁避免出现异常
                    if (ticket > 0) {
                        try {
                            Thread.sleep(100); //每个线程之间睡眠一段时间,避免阻塞
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ":" + ticket--);
                    }
                }
            }
        }
    }
}

6.线程的生命周期
线程包含:新建(new)、准备就绪(runnable)、运行(run)、阻塞(blocking)、终止销毁(terminate)
生命周期图如下:
图片来自:java蚂蚁--线程的生命周期包括哪几个阶段?
6.1
》》》Thread t = new Thread(); ------- 新建状态
》》》t.start(); ------ 运行状态‘’
》》》 t.sleep(3000); ----block状态,需要notify或者notifyall方法重新启动线程
》》》阻塞式方法:
Scanner nextDouble nextFloat next() nextLine(); readLine();
》》》线程结束、jvm崩溃、线程异常—销毁状态
6.2 详细讲解---- 线程的生命周期包括哪几个阶段?(搜索文章)
原创: Java蚂蚁 Java蚂蚁 7月22日

7.线程的状态
7.1 priority优先级:
1》一个线程创建之后,如果不指定线程的优先级,则线程的优先级为5;
2》访问线程对象的优先级:getPriority()
设置线程对象的优先级:setPriority(int priority) priority取值范围为[1-10]
3》线程最大的优先级为10,最小优先级为1,默认为5.
线程优先级越大,被cpu调度执行的概率越高,反之越小(只是被调度执行的概率比较小。不是不调度执行)。
设置优先级的时候,不要超过范围[1-10],不然会抛出异常。
4》可以在线程启动之前设置优先级,也可以在启动之后设置。
5》设置优先级:

 PriorityThread thread = new PriorityThread();
               thread.setPriority(Thread.MAX_PRIORITY);
               thread.start();  
               Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

7.2 join 线程插队
导致当前线程被阻塞,thread实现了一个插队的效果,需要插队的thread 被优先执行了。

 Thread thread = new Thread(new JoinRunnable());
               thread.start(); 
               thread.join(1);

//导致当前线程被阻塞,thread实现了一个插队的效果,thread 被优先执行了。
//插队的线程执行完毕之后,被阻塞的线程才会解除阻塞,进入就绪状态,等待cpu的调度。
7.3 Sleep(long millis):线程休眠,进入阻塞
作用:导致当前线程进入阻塞状态(线程休眠),被阻塞的时间为传入的参数的值。一旦时间到达指定的时间,那么被阻塞的线程转换为就绪状态。

  Thread.sleep(1000);

7.4 yield
yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。
出让当前cpu的控制权,让cpu重新为线程分配资源。先就绪后启动。

Thread.yield();

7.5
线程的分类:可以被分为两类:
1:用户线程=前台线程。
2:守护线程=后台线程
区别:
》》》进程的生命周期只依赖于进程内部的用户线程。如果一个进程中所有的用户线程都死亡了,那么该进程就被jvm杀死了。
》》》如果一个进程中存活的线程只有守护线程了,那么jvm会将进程杀死,进程内部的守护线程一并被杀死了。
》》》main 线程可以为守护线程么?不可以的。主线程是jvm 启动的。不能再设置为守护线程。

 DaemonThread thread = new DaemonThread();
               //设置thread 为守护线程。
               thread.setDaemon(true);
               thread.start();
               
               for(int i=0;i<10;i++){
                       System.out.println(Thread.currentThread().getName());
                       Thread.sleep(200);
               }

8.线程安全:
》》》线程安全问题:指的是多线程的环境的下,多个线程同时访问同一个数据,可能产生数据不同步的问题
》》》ArrayList 是线程不安全的。
A 线程对 ArrayList 进行添加元素 操作。
B 线程对 ArrayList 进行遍历元素的操作。
》》》解决方法
针对线程安全的解决方案:
1:同步代码块
语法:

synchronized (同步监视器){
//同步代码块的内容
}

》》》工作原理解析:
工作的过程:
1:张三先取钱,发现同步监视器 没有上锁,那就对 同步监视器 上锁(添加了一个标识)。并访问同步代码块的内容。
2:张三媳妇来取钱,发现同步监视器 被张三上锁。 只能在 监视器对象上等待(就绪状态)。
3:张三把钱取走了,出了同步代码块,同时将同步监视器解锁。
4:等到cpu 调度执行张三媳妇的时候,发现同步监视器 被解锁了,那么张三媳妇就对同步监视器 加锁并访问同步代码
》》》避免对同一个账户同时进行数据的操作,导致数据丢失或者异常,一般在数据库中常见这个问题,如果数据库事务不加上回滚就会出现这个不同步的问题,导致账户金额数据错乱。
类似于人上厕所,锁隔断小门的过程。
没人,进去锁门,然后开锁,出门。别人看到锁着就等待,。。。

线程安全代码演示:

public class SyTest {
       public static void main(String[] args) {
               Account account = new Account();
               Person zhansan = new Person(account, "张三");
               Person lisi= new Person(account, "李四");
               zhansan.start(); //张三可以取到钱,取完后卡里余额为500
               lisi.start();  //李四无法取钱,因为卡里余额不足
       }
}

//唯一的账户类
class Account{
       private int balance = 1500;//余额
       //取钱的方法
       public void withDraw(int money){
               ***synchronized (Account.class) {***
                       if(balance >= money){
                               try {
                                       Thread.sleep(10);
                               } catch (InterruptedException e) {
                                       e.printStackTrace();
                               }
                               balance -= money;
                               System.out.println(Thread.currentThread().getName()+"取钱成功!余额为:"+balance);
                       }else{
                               System.out.println(Thread.currentThread().getName()+"取钱失败!余额不足,余额为:"+balance);
                       }
               }
       }
}

class Person extends Thread{
       private static Object o = new Object();
       private Account account;
       
       public Person(Account account,String name) {
               this.account = account;
               setName(name);
       }
       @Override
       public void run() {
//                synchronized (Person.class) {
               account.withDraw(1000);
//                }
       }
}

9.死锁–deadlock
具体讲解:https://www.jianshu.com/p/a90803bb0ab7
地址讲解2:https://www.cnblogs.com/bopo/p/9228834.html
》》》举例:
A拿遥控器,锁住,不给B,但是想要B手中的电池;
B拿电池,锁住,不给B,但是想拿A手中的遥控器;
最终都拿不到这个组合资源,实现让空调启动的功能,双层套锁,形成互相制衡,彼此成为彼此的解锁条件。

  public void run() {
               if(id == 0){ //线程-1
                       synchronized(o1){
                                       try {
                                               Thread.sleep(20);
                                       } catch (InterruptedException e) {
                                               e.printStackTrace();
                                       }
                               //TODO
                               System.out.println(Thread.currentThread().getName()+"---o1--->o2");
                               synchronized(o2){
                                     System.out.println(Thread.currentThread().getName()+"---o1--->o2---");
                               }
                       }
               }else{ //线程-2
                       //线程-2 锁住了o2,正在请求锁o1
                       synchronized(o2){  //TODO
                               System.out.println(Thread.currentThread().getName()+"---o2--->o1");
                               synchronized(o1){
                                   System.out.println(Thread.currentThread().getName()+"---o2--->o1---");
                               }
                       }
               }

》》》出现的环境:同步嵌套锁(同步监视器),多个线程。尽量避免。
》》》如何避免:
1.创建线程安全序列
》》》安全状态:如果系统存在 由所有的安全序列{P1,P2,…Pn},则系统处于安全状态。一个进程序列是安全的,如果对其中每一个进程Pi(i >=1 && i <= n)他以后尚需要的资源不超过系统当前剩余资源量与所有进程Pj(j < i)当前占有资源量之和,系统处于安全状态则不会发生死锁。
》》》不安全状态:如果不存在任何一个安全序列,则系统处于不安全状态。他们之间的对对应关系如下图所示:

10.线程通信—多个线程之间进行信息的收发。

public class PipedStreamTest {
       public static void main(String[] args) throws Exception {
               PipedOutputStream pos = new PipedOutputStream();
               //管道输入流和管道输出流,建立关联的方式,通过构造方法,通过对象提供的方法。
               PipedInputStream pis = new PipedInputStream(/*pos*/);
               pis.connect(pos);
               SendThread thread  =new SendThread(pos);
               ReceiveThread thread2 =new ReceiveThread(pis);
               thread2.start();
               Thread.sleep(5000);
               thread.start();
       }
}

//是用来发送数据,
//发送:小鱼儿。
class SendThread extends Thread{
       private PipedOutputStream pos;
       public SendThread(PipedOutputStream pos) {
               this.pos = pos;
       }
       //System.in InputStreamReader +BufferedReader
       //通过键盘输入,将输入的内容,源源不断的发送给 接收方。 输入bye 的时候结束输入。
       @Override
       public void run() {
               System.out.println("SendThread.run()");
               String str = "小鱼儿,你还好么,希望你好好的!";
               try {
//                        pos.write(str.getBytes());
//                        pos.close();
                       send();
               } catch (Exception e) {
                       e.printStackTrace();
               }
       }

       //System.in InputStreamReader +BufferedReader
       //通过键盘输入,将输入的内容,源源不断的发送给 接收方。 输入bye 的时候结束输入。
       private void send() throws Exception{
               BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
               String str = null;
               while((str = br.readLine())!= null){
                       pos.write(str.getBytes());     //str.getBytes()编码
                       pos.flush();
                       if("bye".equals(str))
                               break;
               }
               pos.close();
       }


}

class ReceiveThread extends Thread{
       private PipedInputStream pis;
       public ReceiveThread(PipedInputStream pis) {
               this.pis = pis;
       }
       @Override
       public void run() {
               System.out.println("ReceiveThread.run()");
               byte[] buf = new byte[40];
               try {
                       //输入流,如果输出流没有数据提供,那么就一直等待。
//                        int count = pis.read(buf);
//                        String str = new String(buf,0,count);
//                        System.out.println("收到的信息=="+str);
                       receive();
               } catch (Exception e) {
                       e.printStackTrace();
               }
       }
       
       private void receive() throws Exception{
               byte[] buf = new byte[1024];
               int count = 0;
               while((count = pis.read(buf))!= -1){   //有消息就接收
                       String str = new String(buf,0,count);
                       //把接收到的消息转换成字符串,编码解码的过程
                       System.out.println("收到的信息=="+str);
                       if("bye".equals(str))
                               break;
               }
       }      
}

11.notify
notify():
使用环境:必须在同步代码块或者同步方法内使用。
作用:导致唤醒在当前对象上等待的某个线程,进入就绪状态。
notifyAll():
使用环境:必须在同步代码块或者同步方法内使用。
作用:导致唤醒在当前对象上等待的所有线程,进入就绪状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值