您现在的位置是:网站首页>javajava

java多线程

deling2019年5月19日java240人已围观

简介java多线程

进程介绍

进程:

一个进程就是一个可执行文件(应用程序) 例如: 在我们windows操作系统中,启动网易云音乐就表示启动了一个进程。在Java环境中,我们启动JVM,就表示启动了一个进程,对于我们现在的计算机就是支持多进程的,在同一个操作系统中,可以同时启动多个进程, 例如: 我们打开网易云音乐听歌,我们还可以同时用idea敲代码!

多进程的作用:

单进程计算机只能做一件事。

例如:玩电脑,一=边听歌(音乐进程),一边打游戏(游戏进程)。

对于单核计算机来讲,在同一个时间点来上,游戏进程和音乐进程不是在同时运行,因为在计算机的CPU只能在某个时间点上做一件事,由于计算机将在游戏进程和音乐进程之间频繁的切换执行,切换速度比较快,只是我们感觉游戏和音乐在同时进行而已。

多进程的作用不是提高执行速度,而是提高CPU的使用率

进程和进程之间的内存是独立的

线程介绍

线程:

线程是一个进程中的执行场景,一个进程可以启动多个线程

多线程

多线程作用:

多线程不是为了提高执行速度,而是提高应用程序的使用率

线程和线程共享“堆内存和方法区”,栈内存是独立的,一个线程一个栈

例如:当三个人去访问谷歌浏览器时,如果是单线程,当第一个人在访问的时候,第二个人就不能访问,第三个人也就不能访问,只能等第一个人访问完后,第二个人才能访问,这样说明,我们使用谷歌浏览器的效率变低,为了提高使用率我们就可以用多线程,就是三个人可以一起去访问谷歌浏览器

多个线程之间并不是在同时并发执行,因为由于计算机的切换速度极快,只是我们感觉不到而已

对于java程序的运行原理:

java命令会启动jvm虚拟机,启动jvm,等于启动了一个应用程序,表示启动了一个进程,该进程会自动启动一个“主线程”,然后主线程去调用某个类的main方法,所以main方法运行在主线程中。
在此之前的所有程序都是单线程的。

多线称

多线程的创建和启动

多线程的创建有两种方法:一是继承Thread类, 二是实现Runnalbe接口,推荐使用实现Runnable,因为在实现接口之外还保留了类的继承。

第一种方法:

多线程创建的步骤

1.定义一自定义类继承Thread类

2.重写run()方法

3.创建子类对象

4.启动线程

例如:

public class ThreadDemo1 {    public static void main(String[] args) {         // 创建子类对象
        Thread thread = new Project();        // 启动线程
        thread.start();           // 执行瞬间结束,告诉jvm再分配一个新的栈给t线程
                                 // run方法不用手动调用,系统线程启动后自动调用run方法
        // thread.run();        // 这是普通方法的调用,这样做程序就只有一个线程,run方法结束后,下面的程序才能继续执行
        // 在主线程中运行这段代码
        for (int i = 0; i < 20; i++) {
            System.out.println("main----" + i);
        }
    }    // 有了多线程之后,main方法结束只是结束主线程栈中没有方法栈帧了,
    // 但是其他线程或者其他栈中还有栈帧
    // main方法结束,程序可能还在运行}// 定义一个线程// 自定义类继承Thread,重写run方法,继承Thread,重写run方法class Project extends Thread {    public void run() {        for (int i = 0; i < 100; i++) {
            System.out.println("run--" + i);
        }

    }

}
第二种方式:

多线程的创建:

1.创建一个类实现Runnable接口

2.重写run方法

3.创建线程对象+关联对象

4.启动线程

例如:

public class ThreadDemo2 {    
    /**
     * 推荐使用实现Runnable接口,因为在实现接口之外还保留了类的继承
     */
    public static void main(String[] args) {         // 创建线程对象+关联目标对象
        Thread thread = new Thread(new T());  
        // Thread的构造方法的参数是分配一个目标对象
        // 启动线程
        thread.start();        for (int i = 0; i < 10; i++) {
            System.out.println("主线程的run:" + i);
        }

    }
}// 定义一个t线程// 创建类实现Runnable接口class T implements Runnable {

    @Override    public void run() {        for (int i = 0; i < 30; i++) {
            System.out.println("t线程的run----" + i);
        }
    }
}

线程的生命周期

一个线程对象在它的生命周期内,需要经历5个状态。

新生状态(New)

用new关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。

就绪状态

处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。就绪状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:

  1. 新建线程:调用start()方法,进入就绪状态;

  2. 阻塞线程:阻塞解除,进入就绪状态;

  3. 运行线程:调用yield()方法,直接进入就绪状态;

  4. 运行线程:JVM将CPU资源从本线程切换到其他线程。

运行状态(Running)

在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。

阻塞状态(Blocked)

阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。有4种原因会导致阻塞:

  1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。

  2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。

  3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。

  4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。

死亡状态(Terminated)

死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它run()方法内的全部工作; 另一个是线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程(注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。 当一个线程进入死亡状态以后,就不能再回到其它状态了。

多线程的阻塞(sleep)

sleep方法: sleep方法是一个静态方法,它的作用是阻塞当前线程,腾出cpu,让给其他线程

public class ThreadDemo2 {
    public static void main(String[] args) throws InterruptedException {        
        // 创建线程对象+关联对象
        Thread thread = new Thread(new T());        
        // 设置线程对象的名字
        thread.setName("java");        
        // 启动线程
        thread.start();
        for (int i = 0; i < 21; i++) {            
        System.out.println(Thread.currentThread().getName() + "====" + i);            
            // 这里的异常就可以在方法的声明位置上抛出异常
            // 让主线程阻塞
            Thread.sleep(500);
        }
    }

}
class T implements Runnable {
    @Override    
    // Tread中的run方法不抛出异常,所以重写run方法之后,在Run方法的声明位置上不能使用throws
    // 所以run方法中的异常只能try...catch
    public void run() {
        for (int i = 0; i < 10; i++) {            
        // 返回对当前正在执行的线程对象的引用。
            System.out.println(Thread.currentThread().getName() + "------" + i);            
            try {                
                   Thread.sleep(1000); // 当前线程阻塞1s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
interrupt方法:打断正在休眠的线程,中断这个线程。(依靠的异常处理机制)
public class ThreaDemo3 {   
     public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new T2());
        thread.setName("java");
        thread.start();        
        // 1s之后,记得要抛出异常
        Thread.sleep(1000);        
        // 打断T2线程的阻塞
        thread.interrupt();

    }

}class T2 implements Runnable {    
    @Override
    public void run() {        
        try {       
            // 阻塞 5s
            Thread.sleep(5000);
            System.out.println("========");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }        
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}
interrupt方法:正确的终止一个正在执行的线程,通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程的运行。
public class ThreaDemo3 {    
    public static void main(String[] args) throws InterruptedException {
            T2 t2 = new T2();
            Thread thread = new Thread(t2);
            thread.setName("java");
            thread.start();        
            // 5s之后
            Thread.sleep(5000);        
            // 终止
            t2.run = false;


    }

}
class T2 implements Runnable {    
    //标记变量,表示线程是否可中止;
    boolean run = true;    
    @Override
    public void run() {        
        for (int i = 0; i < 10; i++) {            
            //当run的值是true时,继续线程体;false则结束循环,继而终止线程体;
            if (run) {                
                try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {      
                        return;
                }
                System.out.println(Thread.currentThread().getName() + "--->" + i);
            }
    }
}

yield方法 :该方法也是一个静态方法,作用给同一个优先级的线程让位。但是让位时间不要固定 和sleep方法区别:yield方法时间不固定

public class ThreaDemo3 {
    public static void main(String[] args) {
        T2 t2 = new T2();
        Thread thread = new Thread(t2);
        thread.setName("java");
        thread.start();
        for (int i = 0; i < 100; i++) {            
            System.out.println(Thread.currentThread().getName() + "====" + i);
        }
    }
}class T2 implements Runnable {
    @Override
    public void run() {

        for (int i = 0; i < 100; i++) {           
         System.out.println(Thread.currentThread().getName() + "--->" + i);           
              if (i % 20 == 0) {           
                       Thread.yield();
            }
        }
    }
}
join方法: 是一个成员方法,当前线程可以调用另一个线程的join方法,调用后当前线程会被阻塞不再执行,直到被调用的线程执行完毕,当前线程才会执行
public class ThreaDemo3 {

    public static void main(String[] args) throws InterruptedException {
        T2 t2 = new T2();
        Thread thread = new Thread(t2);
        thread.setName("java");
        thread.start();        // 合并T2线程和主线程,谁先合并,就会先执行完,然后再执行本线程,这样就变成单线程的程序,就相当于两个栈空间变为一个栈区
        thread.join();
        for (int i = 0; i < 10; i++) {           
             System.out.println(Thread.currentThread().getName() + "====" + i);
        }
    }

}
class T2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {           
             try {            
                 Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }           
               System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

线程的优先级

  1. 处于就绪状态的线程,会进入“就绪队列”等待JVM来挑选。

  2. 线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。

  3. 使用下列方法获得或设置线程对象的优先级。

int getPriority();

void setPriority(int newPriority);

注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。

线程的同步

线程异步:  两个线程互不干扰,自己执行自己的,两个线程之间谁也不等谁

例如:t1线程和t2线程,t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁

线程同步:  处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

例如:t1线程和t2线程执行,当t1线程必须等t2线程执行结束之后,t1线程才能执行,这就是线程同步执行

线程同步能是数据安全,尽管应用程序的使用率降低,但是为了保证数据安全,必须加入线程同步机制,线程同步机制使程序编程了(等同)单线程。

使用线程同步的三个条件

1.必须是多线程环境 。

2.多线程必须共享同一个数据。

3.共享的数据涉及到修改操作。

例如: 利用线程同步实现银行取钱

/**
 * 利用线程同步实现银行去钱
 */
 public class ThreaDemo3 {   
        private String name;   // 账户名字
        private double banlance;  // 账户金额
    // 无参构造方法
    public ThreaDemo3() {
    } 
    // 带参构造方法   
    public ThreaDemo3(String name, double banlance) {       
         this.name = name;     
         this.banlance = banlance;
    }   
    public void setName(String name) {      
        this.name = name;
    }    
    public String getName() {        
        return name;
    }    
    public void setBanlance(double banlance) {      
         this.banlance = banlance;
    }  
    public double getBanlance() {     
        return banlance;
    }   
     // 模拟取款操作
    public void withMoney(double money) {        
    //把需要同步的代码,方法同步语句块中
        synchronized (this) {            
        // 取完钱后的余额为 =账户钱-取的钱
         double before = banlance - money;
            try {
                // 延迟
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }            
            // 更新余额
            this.setBanlance(before);
        }
    }    
    // 模拟取款线程
    static class T1 implements Runnable {        
    //  要取钱的账户
        ThreaDemo3 ban;        
        // 无参构造方法
        T1() {
        }        
        // 带参构造方法
        T1(ThreaDemo3 ban) {            
            this.ban = ban;
        }        
        @Override
        public void run() {
            ban.withMoney(1000);
            System.out.println("取款1000.0成功!余额为" + ban.getBanlance());
        }
    }    
    public static void main(String[] args) {        
        // 创建一个共同的账户
        ThreaDemo3 threaDemo3 = new ThreaDemo3("user-001", 5000);        // 创建线程对同一个账户取款
        T1 t1 = new T1(threaDemo3);
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t1);
        thread1.start();
        thread2.start();

    }
}

synchronized关键字(对象锁)

第一种方法:synchronized语句块

如果一个线程执行到有synchronized关键字,就会去找this的对象锁,如果找到this对象锁,即进入同步语句块中执行程序,当同步语句块中的代码执行结束后,这个线程就归还this的对象锁。

例如:t1线程和t2线程 当t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到synchronized关键字,所以也去找this的对象锁,但是该对象锁被t1线程持有,只能在这等待this对象的归还。

第二种方法:synchronized 方法

synchronized关键字添加到成员方法上,线程拿走的也是this的对象锁

建议:使用第一种方法,因为第一种语句块能精确到具体的代码块

注意:线程同步的synchronized关键字需要等待的条件:

1.两个线程共享同一个对象数据

2.需要用synchronized关键字后才能需要等待,没有synchronized关键字就不需要等待

类锁

类锁,类只有一个, 所以锁是类级别的,只有一个

synchronized关键字添加到静态方法上,线程执行此方法的时候会找类锁,这里和对象锁不一样, 类锁可以不共享一个对象数据,因为类和对象无关,当两个方法都是用了synchronized关键字,就需要等待类锁。当第一个静态方法使用了synchronized关键字,而另一个方法没使用synchronized关键字,第一个静态方法也不用等第二个静态方法。

死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

例如:

public class ThreadDemo5 {
    public static void main(String[] args) throws InterruptedException {     
         Object o1 = new Object();
         Object o2 = new Object();
        Thread thread1 = new Thread(new A1(o1, o2));
        Thread thread2 = new Thread(new A2(o1, o2));
        thread1.start();
        thread2.start();
    }

}
class A1 implements Runnable {   
     Object o1;    Object o2;
    A1() {

    }
    A1(Object o1, Object o2) {     
       this.o1 = o1;   
       this.o2 = o2;
    }   
     @Override
    public void run() {
        synchronized (o1) {            
        try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2) {
            }
        }

    }
}
class A2 implements Runnable {    
    Object o1;    
    Object o2;

    A2() {
    }

    A2(Object o1, Object o2) {        。
        this.o1 = o1;        
        this.o2 = o2;
    }    
    @Override
    public void run() {
        synchronized (o2) {            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1) {
            }
        }
    }
}

死锁解决方法

死锁是由于“同步块需要同时持有多个对象锁造成”的,要解决这个问题,思路很简单,就是:同一个代码块,不要同时持有两个对象锁。

例如:

public class ThreadDemo5 {

    public static void main(String[] args) throws InterruptedException {      
        Object o1 = new Object();    
        Object o2 = new Object();
        Thread thread1 = new Thread(new A1(o1, o2));
        Thread thread2 = new Thread(new A2(o1, o2));
        thread1.start();
        thread2.start();
    }

}
class A1 implements Runnable {   
     Object o1;    
     Object o2;
    A1() {

    }

    A1(Object o1, Object o2) {        
    this.o1 = o1;        
    this.o2 = o2;
    }   
     @Override
    public void run() {
        synchronized (o1) {            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        synchronized (o2) {
        }

    }
}
class A2 implements Runnable {    
    Object o1;    
    Object o2;

    A2() {
    }

    A2(Object o1, Object o2) {        
        this.o1 = o1;        
        this.o2 = o2;
    }   
     @Override
    public void run() {
        synchronized (o2) {            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        synchronized (o1) {
        }
    }
}

守护线程

其他所有的用户线程结束,守护线程就退出!

守护线程一般都是无限执行的

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread thread = new Thread(new A1());      
         // 设置线程名字
        thread.setName("java");        
        // 将用户线程设置为守护线程
        thread.setDaemon(true);        
        // 启动线程
        thread.start();
        for (int i = 0; i <= 5; i++) {            
            // 打印输出当前线程的名字
             System.out.println(Thread.currentThread().getName() + "====" + i);           
                 try {                // 休眠1s之后
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }

}
class A1 implements Runnable {
    @Override
    public void run() {        
    int i = 0;        
    // 一直循环,只有当上面的主线程结束,用户线程才会结束
        while (true) {            
            System.out.println(Thread.currentThread().getName() + "====" + i);           
             try {               
                  Thread.sleep(500);
                } catch (InterruptedException e) {
                e.printStackTrace();
                }
            }
        }
}

定时器(Timer)的应用

作用:每隔一段固定时间执行一段代码

例如:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class ThreadDemo5 {    
    public static void main(String[] args) throws ParseException {        
        // 定义一个定时器
        Timer timer = new Timer();
        timer.schedule(                
        // 指定定时任务
            new TimerTest(),                
            // 指定任务开始时间
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2019-2-10 23:12:00 000"),                
            //间隔时间  
            10 * 1000);
    }

}
// 一个非抽象类继承一个抽象类需要重写抽象的方法
class TimerTest extends TimerTask {
    @Override    
    public void run() {        
    // 任务代码
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));
    }
}


Tags: java

很赞哦! (4)

上一篇:IO流介绍

下一篇:完整JDBC的使用和步骤

留言

来说点儿什么吧...

您的姓名: *

选择头像: *

留言内容:

    2019年2月25日 13:35嘿嘿

    2019年2月26日 13:20ok

    可以可以!

    2019年3月18日 09:2311

    1

    2019年3月28日 09:24www.ikeguang.com

    可以可以

    2019年5月29日 18:47qwe

    666

    2019年5月30日 16:52BlankYk

    he,tui~

    2019年5月30日 17:04123

    321

    2019年6月26日 10:02周树人

    厉害厉害

    2019年6月26日 10:34sdlakrj

    sdaag

    2019年6月29日 15:31sdagafdbaf

    dgafdgdfh

    站长回复:你这是什么什么高级语言,我表示看不懂哈哈

    2019年7月6日 16:37啦啦

    写的真好!谢谢博主

    站长回复:谢谢!

    2019年8月14日 12:35傻傻

    厉害 小林