JAVAEE多线程

线程:

原理:多线程执行时候,在栈内存中,其实每一个执行线程都有一片所属的栈内存的空间,进行方法的

压栈,和弹栈.

原理图;

 

 

Thread类:

构造方法:

 

  • public Thread():分配一个新的线程对象。

  • public Thread(String name):分配一个指定名字的新的线程对象。

  • public Thread(Runnable target):分配一个带有指定目标新的线程对象。

  • public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。

常用方法:

public String getName():获取当前线程名称。

public static Thread currentThread():返回对当前正在执行的线程对象的引用。

public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。

public void run():此线程要执行的任务在此处定义代码。

public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

注意:如果一个类继承Thread,则不适合资源的共享,但是实现了Runable接口的话,则很容易的实现资源共享.

 

创建多线程:

 

多线程三种创建方法:

package com.heima.biji;
//1,通过继承的方式创建线程
public class Demo01 {
    public static void main(String[] args) {
        //创建自定义对象
        Mythread01 mythread01 = new Mythread01("德玛");
        //创建线程对象
        Thread thread = new Thread(mythread01);
        //开启线程
        thread.start();
        //在主方法体中执行for循环
        for (int j = 0j < 50j++) {
            System.out.println("main线程:"+j);
        }
    }
}

package com.heima.biji;
public class Mythread01 extends Thread {
    //定义指定的线程名称的构造方法
    public Mythread01(String name){
        //调用父类的String参数的构造方法,指定线程的名称
        super(name);
    }
        //重写run方法,完成该线程的执行的逻辑
    @Override
    public void run() {
        for (int i = 0i < 50i++) {
            System.out.println(getName()+":正在执行"+i);
        }
    }
}

package com.heima.biji;
//3.匿名内部内方法创建线程
public class Demo03 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0i < 50i++) {
                    System.out.println("子类中的:"+i+"循环");
                }
            }
        };
        //创建线程的对象并调用runnable.
        Thread thread = new Thread(runnable);
        //启动线程
        thread.start();
        for (int j = 0j < 50j++) {
            System.out.println("父类中的:"+j+"循环");
        }
    }
}

 

package com.heima.biji;
//2.通过接口的方式
public class Demo02 {
    public static void main(String[] args) {
        //创建实现内的对象
        Mythread02 mythread02 = new Mythread02( );
        //创建线程的对象
        Thread thread = new Thread(mythread02);
        //启动线程
        thread.start();
        for (int j = 0j < 50j++) {
            System.out.println("main方法的线程:"+j);
        }
    }
}

package com.heima.biji;
    //实现类
public class Mythread02 implements Runnable{
    //重写run方法
    @Override
    public void run() {
        for (int i = 0i < 50i++) {
            //Thread.currentThread().getName是通过Thread类中构造方法获取
            //currentThread,来返回当前正在执行的线程对象的引用来获取当前线程的名称.
            //System.out.println(Thread.currentThread().getName( )+" "+i);
            System.out.println("子线程的j第:"+i+"循环");
        }
    }

}

三种方式各自好处:

继承

 

 

 

接口

 

 

匿名内部内

 

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

 

实现Runnable接口比继承Threed类所具有的优势

 

1.适应多个相同的程序代码的线程去共享一个资源.

2.可以避免java中的单继承的局限性.

3.增加程序的健壮性,实现解耦操作,任务可以被多个线程共享,任务和线程独立.

4.线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类

耦合性:类和类之间的相互影响的程度.

扩充:在java中,每次程序运行至少启动2个线程,一个是main线程,一个是垃圾收集线程,

因为每当使用Java命令执行一个类的时候,一个是垃圾收集线程,因为每当使用Java执行一个类

实际东辉启动一个jvm,每个JVM其实都是在操作系统启动一个进程.

 

线程安全:

定义:

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

实例题目:

 

 


 

线程同步:

 

当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题

解决:

解决上诉问题多线程访问一个资源安全性问题,也就是解决重复的问题,java中提供了(synchronized)来解决

线程访问使用三种方式完成同步操作

1.同步代码快(synchronized)关键字可以用于方法的某个区块中表示区块资源的互斥访问.

格式:

 

synchronized(同步锁){

需要同步操作的代码

}

同步锁:

对象的同步锁只是一个概念,可以想象为在对象上标志记了一个锁

.锁对象(可以是任意数据对象)

.多个线程对象,要使用同一把锁.

注意:在任何时候,最多仍许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLDCKED).

2.同步方法

3.锁机制

 

同步方法:

 


 

同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

 

public synchronized void method(){

可能会产生线程安全问题的代码

}

同步锁是谁:

对于非static方法,同步锁就是this.

对于static方法,我们使用当前方法所有类的字节代码对象(类名.class)

 

 


 

 

package com.heima.biji;

//同步方法

public class Mythread04 implements Runnable{

        private int ticket = 100;//定义电影票的个数

        //重写run方法

        @Override

        public void run() {

            //每个窗口买票的过程

            //窗口永远在开启

            while (true){

                sellTicket();

            }

        }

        public synchronized void sellTicket(){

            if(ticket>0){

                try {

                    Thread.sleep(100);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

                //获取当前线程对象的名字

                String name = Thread.currentThread().getName();

                System.out.println(name + "正在买:" + ticket-- + "张");

            }

        }

    }

 

Lock锁:

java.util.concurrent.loks.Lock机制提供了比synchronized代码块,和synchronized方法

Lock锁也称同步锁,加锁与释放锁方法化了,如下:

public void lock():加同步锁。

 lock他是一个接口,他只能new 实现类ReentrantLock

public void unlock():释放同步锁。

代码快:

 

package com.heima.biji;

import java.lang.invoke.VarHandle;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

 

//Lock锁

public class Mythread05 implements Runnable {

    //设置票数

    private int ticket = 100;

    Lock lock = new ReentrantLock();

 

    //执行买票操作

    @Override

    public void run() {

        while (true) {

            lock.lock();//加同步锁

            if (ticket > 0) {//有票可以买

                //每个窗口买票的操作

                //窗口 永远开启

                try {

                    Thread.sleep(100);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

                //获取当前线程对象的名字

                String name = Thread.currentThread().getName();

                System.out.println(name + "正在买:" + ticket-- + "票");

            }

            lock.unlock();//释放同步锁

        }

    }

}
 

 

线程的状态:

当线程被创建并启动后,他既不是一启就进入执行状态,也不是一直处于执行状态:

在线程生命周期中有6种状态.

 

线程状态

导致方式条件

NEW(新建)

线程刚被创建,但是并未启动。还没调用start方法。

Runnable(可运行)

线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。

Blocked(锁阻塞)

当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。

Waiting(无限等待)

一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。通过wait()方法进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。

TimedWaiting(计时等待)

同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。

   Teminated(被终止)       :        因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

 


 

Timed Waiting(计时等待):

  • Timed Waiting在API中的描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。单独的去理解这句话,真是玄之又玄,其实我们在之前的操作中已经接触过这个状态了,在哪里呢?

 


代码:题目

 

 

**实现一个计数器,计数到100,在每个数字之间暂停1秒,每隔10个数字输出一个字符串**

package com.heima.biji;
//实现一个计数器,计数到100,在每个数字之间暂停1秒,每隔10个数字输出一个字符串
public class Demo05 extends Thread {
    //重写run
    @Override
    public void run() {
        int [] num=new int[100];
        for (int i = 1i <=num.lengthi++) {
            if((i)%10==0) {
                System.out.println(i);
                System.out.println("--------------");
            }
            System.out.print(i+" ");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        Demo05 demo05 = new Demo05();
        demo05.start();
    }
}

通过以上代码发现:

 

- 进入 TIMED_WAITING 状态的一种常见情形是调用的 sleep 方法,单独的线程也可以调用,不一定非要有协作关系。

- 为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠

- sleep与锁无关,线程睡眠到期自动苏醒,并返回到Runnable(可运行)状态。

提示:

sleep()中指定的时间线程不会运行的最短时间,因此,sleep()方法不能保证线程到期后立即执行.

Blocked线程图:

 

 


 

Waiting(无限等待):

模拟无限等待:

1.使用锁对象调用wait()方法,则该线程就会进入无限等待状态

2.其他线程使用锁对象调用notify()或者notifyAll()方法,才可以唤醒无限等待

notift()唤醒单个无限等待的线程

notifyAll()唤醒多个无限等待的线程

3.调用wait()和notify()以及notifyAll()方法的锁对象要一致.

4.无限等待,可以切换到可运行或者锁阻塞状态.

 

package com.heima.biji;
//Wating状态在API中介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
public class Demo06 {
    public static Object obj new Object();//锁对象

    public static void main(String[] args) {

        // 演示waiting匿名内部类

        new Thread(new Runnable() {
            @Override
            public void run() {
               
while (true){
                   
synchronized (obj){
                       
try {
                            System.
out.println( Thread.currentThread().getName() +"=== 获取到锁对象,调用wait方法,进入waiting状态,释放锁对象");
                            obj.wait() //无限等待
                            //obj.wait(5000); //计时等待, 5秒 时间到,自动醒来

                        catch (InterruptedException e) {
                            e.printStackTrace()
;
                        }
                        System.
out.println( Thread.currentThread().getName() + "=== 从waiting状态醒来,获取到锁对象,继续执行了");
                    }
                }
            }
        }
,"等待线程").start();

        new Thread(new Runnable() {
           
@Override
            public void run() {
//                while (true){   //每隔3秒 唤醒一次
                try {
                    System.
out.println( Thread.currentThread().getName() +"----- 等待3秒钟");
                    Thread.sleep(3000);
                catch (InterruptedException e) {
                    e.printStackTrace()
;
                }
               
synchronized (obj){//同步代码块
                    System.
out.println( Thread.currentThread().getName() +"----- 获取到锁对象,调用notify方法,释放锁对象");
                    obj.notify();
                }
            }

//            }
        },"唤醒线程").start();
    }
}

 

线程的有线通信:

 

补充知识点:

 


1.多线程的开启:

题目:描述Thread类中的start()方法与run()方法的区别

 

答:线程对象调用的run()方法不开启线程,仅是对象的调用方法,线程对象调用start()方法启用线程,并jvm调用run()方法在开启的线程中执行.

2.

经典题目:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值