关于JAVA线程的学习

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于JAVA线程的学习相关的知识,希望对你有一定的参考价值。

关于JAVA线程的学习

4、多线程

4.1、什么是进程?什么是线程?
进程是一个应用程序(1个进程是一个软件)。
线程是一个进程中的执行场景/执行单元。
一个进程可以启动多个线程。

4.2、对于java程序来说,当在DOS命令窗口中输入:
java HelloWorld 回车之后。
会先启动JVM,而JVM就是一个进程。
JVM再启动一个主线程调用main方法。
同时再启动一个垃圾回收线程负责看护,回收垃圾。
最起码,现在的java程序中至少有两个线程并发,
一个是垃圾回收线程,一个是执行main方法的主线程。

4.3、进程和线程是什么关系?举个例子

阿里巴巴:进程
马云:阿里巴巴的一个线程
童文红:阿里巴巴的一个线程

京东:进程
强东:京东的一个线程
妹妹:京东的一个线程

进程可以看做是现实生活当中的公司。
线程可以看做是公司当中的某个员工。

注意:
进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!)
魔兽游戏是一个进程
酷狗音乐是一个进程
这两个进程是独立的,不共享资源。

线程A和线程B呢?
在java语言中:
线程A和线程B,堆内存和方法区内存共享。
但是栈内存独立,一个线程一个栈。

假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
互不干扰,各自执行各自的,这就是多线程并发。

火车站,可以看做是一个进程。
火车站中的每一个售票窗口可以看做是一个线程。
我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。
所以多线程并发可以提高效率。

java中之所以有多线程机制,目的就是为了提高程序的处理效率。

4.4、思考一个问题:
使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。
main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在
压栈弹栈。

4.5、分析一个问题:对于单核的CPU来说,真的可以做到真正的多线程并发吗?

对于多核的CPU电脑来说,真正的多线程并发是没问题的。
4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。

什么是真正的多线程并发?
t1线程执行t1的。
t2线程执行t2的。
t1不会影响t2,t2也不会影响t1。这叫做真正的多线程并发。

单核的CPU表示只有一个大脑:
不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。
对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于
CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情
同时在做!!!!!
线程A:播放音乐
线程B:运行魔兽游戏
线程A和线程B频繁切换执行,人类会感觉音乐一直在播放,游戏一直在运行,
给我们的感觉是同时并发的。

电影院采用胶卷播放电影,一个胶卷一个胶卷播放速度达到一定程度之后,
人类的眼睛产生了错觉,感觉是动画的。这说明人类的反应速度很慢,就像
一根钢针扎到手上,到最终感觉到疼,这个过程是需要“很长的”时间的,在
这个期间计算机可以进行亿万次的循环。所以计算机的执行速度很快。

5、java语言中,实现线程有两种方式,那两种方式呢?

java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
   // 定义线程类
     public class MyThread extends Thread{
          public void run(){

        }
   }
   // 创建线程对象
   MyThread t = new MyThread();
   // 启动线程。
   t.start();

 1 public class ThreadTest01 {
 2 
 3     public static void main(String[] args) {
 4         //main方法,这里代码属于主线程
 5         //新建分支线程
 6         MyThread myThread= new MyThread();
 7         
 8         //启动线程 start()启动一个分支线程,在JVM中开辟空间,只是为了开辟新空间,这段代码一瞬间消失
 9         //启动成功就会消失,会直接调用run方法,并且run方法在分支的栈底部(压栈)
10         //run方法和main方法平级
11         
12         myThread.start();
13         //运行在主支栈
14         for(int i=0;i<1000;i++){
15             System.out.println("主线程---->"+i);
16             }
17           }
18        }
19 class MyThread extends Thread{
20     @Override
21     public void run() {
22       //运行在分支栈
23         for(int i=0;i<1000;i++){
24             System.out.println("分支线程---->"+i);
25         }
26     }
27 }


第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。
   // 定义一个可运行的类
        public class MyRunnable implements Runnable {
             public void run(){

          }
        }
    // 创建线程对象
         Thread t = new Thread(new MyRunnable());
         // 启动线程
           t.start();

 1 public class ThreadTest02 {
 2 
 3     public static void main(String[] args) {
 4         //创建一个可运行对象
 5         //MyRunnable r=new MyRunnable();
 6         //将可运行的对象封装成一个线程对象
 7         //Thread t=new Thread(r);
 8         Thread t=new Thread(new MyRunnable());//合并代码
 9         //启动线程
10         t.start();
11         //运行在主支栈
12             for(int i=0;i<100;i++){
13             System.out.println("主线程---->"+i);
14         }
15                   
16         
17     }
18 
19     
20 }
21 //这并不是一个线程,是一个可运行的类.他还不是一个线程
22 class MyRunnable implements Runnable{
23 
24     @Override
25     public void run() {
26         //运行在分支支栈
27             for(int i=0;i<100;i++){
28         System.out.println("分支线程---->"+i);
29     }
30                   
31     }
32     
33 }

        通过匿名内部类:

 

 1 /*
 2  * 采用匿名内部类
 3  */
 4 public class ThreadTest03 {
 5 
 6     public static void main(String[] args) {
 7         //创建线程对象,采用匿名内部类方式
 8         //这是通过一个没有名字的类new出来的对象
 9     Thread t=new Thread(new Runnable() {
10         
11         @Override
12         public void run() {
13             
14             //运行在支栈
15                 for(int i=0;i<100;i++){
16                 System.out.println("分支线程---->"+i);
17             }
18         }
19     });
20     //启动线程
21     t.start();
22     
23     //运行在主栈
24     for(int i=0;i<100;i++){
25     System.out.println("主线程---->"+i);
26 }
27     
28     }
29 
30 }   

   注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承
                其它的类,更灵活。

6、关于线程对象的生命周期?
     新建状态
     就绪状态
     运行状态
     阻塞状态
     死亡状态

 

线程第二天学习

1、(这部分内容属于了解)关于线程的调度

1.1、常见的线程调度模型有哪些?

   抢占式调度模型:
  那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
  java采用的就是抢占式调度模型。

   均分式调度模型:
  平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
  平均分配,一切平等。
  有一些编程语言,线程调度模型采用的是这种方式。

   怎么获取线程:

public static void main(String[] args) {
        //获取当前线程
        Thread tt=Thread.currentThread();
        System.out.println(tt.getName());
        //创建线程对象
        MyThread2 t=new MyThread2();
        t.setName("t1");
        String name = t.getName();
        System.out.println(name);
        t.start();
        MyThread2 t2=new MyThread2();
        System.out.println(t2.getName());
        t2.start();
    }

}
class MyThread2 extends Thread{
        public void  run(){
            Thread ttt=Thread.currentThread();
            System.out.println(ttt.getName());
            //运行在支栈
            for(int i=0;i<100;i++){
                System.out.println(ttt.getName()+"分支线程---->"+i);
            
            }
              }
        }
View Code

1.2、java中提供了哪些方法是和线程调度有关系的呢?

  实例方法:
     void setPriority(int newPriority) 设置线程的优先级
     int getPriority() 获取线程优先级
         最低优先级1
         默认优先级是5
         最高优先级10

 1 /**
 2  * 关于线程优先级
 3  * @author yumu
 4  *
 5  */
 6 public class ThreadTest09 {
 7 
 8     public static void main(String[] args) {
 9     //设置main支线为1
10     Thread.currentThread().setPriority(1);
11     //获取当前优先级
12     Thread cru=Thread.currentThread();    
13     System.out.println(cru.getName());
14     Thread t=new Thread(new MyRunnable5());
15     //设置分线优先级为10
16     t.setPriority(10);
17     t.setName("t");
18     t.start();
19     //优先级高,抢到CPU时间相对多一些,运行时间多一些
20     for(int i=0;i<1000;i++){
21         System.out.println(Thread.currentThread().getName()+i);
22         
23     }
24     }
25 
26 }
27 class MyRunnable5 implements Runnable{
28 
29     @Override
30     public void run() {
31         //获取线程优先级
32         //System.out.println(Thread.currentThread().getName()+"线程优先级是"+Thread.currentThread());
33         for(int i=0;i<1000;i++){        
34             System.out.println(Thread.currentThread().getName()+i);
35         }
36     }
37     
38 }

 


     优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)

   静态方法:

   static void sleep(long millis): 

               使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 
 1 /**
 2  * 关于线程sleep方法
 3  *  static void sleep(long millis)
 4  *    1.静态方法:
 5  *    2.参数毫秒
 6  *    3.让当前线程进入休眠,进入阻塞状态
 7  *    4.Thread.sleep(),固定时间调用某方法
 8  *    
 9  * @author yumu
10  *
11  */
12 public class ThreadTest05 {
13 
14     public static void main(String[] args) throws InterruptedException {
15         Thread.sleep(1000*5);
16     System.out.println("5秒后执行");
17     
18     for(int i=0;i<10;i++){
19         System.out.println(Thread.currentThread().getName()+"----->"+i);
20         //睡眠一秒
21         Thread.sleep(1000);
22         
23     }
24     }
25 }

如何中断sleep();

 1 /**
 2  * 中断sleep的睡眠
 3  * @author yumu
 4  * 在java中怎么强制终止线程的执行
 5  * 
 6  * t.stop()  //已过时,不建议使用
 7  *   这种方式容易丢失数据,不建议使用,因为直接杀死线程
 8  * t.interrupt();  //干扰睡眠  
 9  *
10  */
11 public class ThreadTest07 {
12 
13     public static void main(String[] args) {
14         Thread t=new Thread(new MyRunnable2());
15         t.setName("t");
16         t.start();
17         
18         try {
19             Thread.sleep(1000*5);
20         } catch (InterruptedException e) {
21             
22             e.printStackTrace();
23         }
24         //中断线程的睡眠  (靠的是异常处理机制)
25         t.interrupt();  //干扰睡眠  
26     }
27 
28 }
29 class MyRunnable2 implements Runnable{
30 
31     @Override
32     public void run() {
33         System.out.println(Thread.currentThread().getName()+"----->begin");
34         try {
35             Thread.sleep(1000*60*60*24);
36         } catch (InterruptedException e) {
37             
38             e.printStackTrace();
39         }
40         System.out.println(Thread.currentThread().getName()+"----->end");
41     }
42     
43 }

 

 如何合理的终止线程:  打一个布尔标记

 1 /**
 2  * 如何合理的终止线程
 3  * @author yumu
 4  *
 5  */
 6 public class ThreadTest08 {
 7 
 8     public static void main(String[] args) {
 9         MyRunnable3 r=new MyRunnable3();
10         Thread t=new Thread(r);
11         t.start();
12         
13         try {
14             Thread.sleep(1000*5);
15         } catch (InterruptedException e) {
16             
17             e.printStackTrace();
18         }
19         //终止线程.改标记
20         r.run=false;
21         
22     }
23 
24 }
25 class MyRunnable3 implements Runnable{
26     //打一个布尔标记
27     boolean run=true;
28     @Override
29     public void run() {
30         
31         for(int i=0;i<10;i++){
32             if(run){
33             System.out.println(Thread.currentThread().getName()+"--->"+i);
34             try {
35                 Thread.sleep(1000);
36         } catch (InterruptedException e) {
37                 
38                 e.printStackTrace();
39             }
40         
41         }else{
42             
43             
44             return;
45         }
46     }
47     }
48 }

 

        static void yield() 让位方法
      暂停当前正在执行的线程对象,并执行其他线程
         yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
         yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。

1 for(int i=0;i<1000;i++){
2             //每100让位一次
3             if(i%100==0){
4                 Thread.yield();//当前线程暂停一下,给主线程让一下
5             }
6             System.out.println(Thread.currentThread().getName()+i);
7         }

 


注意:在回到就绪之后,有可能还会再次抢到。

   实例方法:
       void join()
      合并线程
           class MyThread1 extends Thread {
           public void doSome(){
                  MyThread2 t = new MyThread2();
                      t.join(); // 当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以继续。
               }
          }

           class MyThread2 extends Thread{

     }

 1 /**
 2  * 合并线程  join()
 3  * @author yumu
 4  *
 5  */
 6 public class ThreadTest10 {
 7 
 8     public static void main(String[] args) {
 9         System.out.println("main begin");
10         Thread t=new Thread(new MyRunnable6());
11         t.setName("t");
12         t.start();
13         try {
14             t.join();  //t线程合并到当前线程
15         } catch (InterruptedException e) {
16             
17             e.printStackTrace();
18         }
19         System.out.println("main over");
20     }
21 
22 }
23 class MyRunnable6 implements Runnable{
24 
25     @Override
26     public void run() {
27         for(int i=1;i<5;i++){
28             System.out.println(Thread.currentThread().getName());
29         }
30     }
31     
32 }

 

 

 

2、关于多线程并发环境下,数据的安全问题。(重点)

2.1、为什么这个是重点?
 以后在开发中,我们的项目都是运行在服务器当中,
 而服务器已经将线程的定义,线程对象的创建,线程
 的启动等,都已经实现完了。这些代码我们都不需要
 编写。

最重要的是:你要知道,你编写的程序需要放到一个
多线程的环境下运行,你更需要关注的是这些数据
在多线程并发的环境下是否是安全的。(重点:*****)

2.2、什么时候数据在多线程并发的环境下会存在安全问题呢?
三个条件:
条件1:多线程并发。
条件2:有共享数据。
条件3:共享数据有修改的行为。

满足以上3个条件之后,就会存在线程安全问题。

2.3、怎么解决线程安全问题呢?
当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在
线程安全问题,怎么解决这个问题?
线程排队执行。(不能并发)。
用排队执行解决线程安全问题。
这种机制被称为:线程同步机制。

专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。

怎么解决线程安全问题呀?
使用“线程同步机制”。

线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全
第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。

案例:模拟银行取款:

 1 package javase.threadsafe;
 2 /*
 3  * 银行账户
 4  */
 5 public class Account {
 6     //账号
 7     private String actno;
 8     //余额
 9     private double balance;
10     public Account(String actno, double balance) {
11         super();
12         this.actno = actno;
13         this.balance = balance;
14     }
15     public Account() {
16         super();
17         
18     }
19     public String getActno() {
20         return actno;
21     }
22     public void setActno(String actno) {
23         this.actno = actno;
24     }
25     public double getBalance() {
26         return balance;
27     }
28     public void setBalance(double balance) {
29         this.balance = balance;
30     }
31     @Override
32     public String toString() {
33         return "Account [actno=" + actno + ", balance=" + balance + "]"关于java多线程初试

关于学习Java多线程这一篇就够了

#yyds干货盘点# Java | 关于synchronized相关理解

关于JAVA线程的学习

线程学习知识点总结

Java线程池详解