前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java多线程

java多线程

作者头像
不期而遇丨
发布2022-09-09 18:00:56
8730
发布2022-09-09 18:00:56
举报

java多线程

进程与线程

进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期 线程:进程可进一步细化为线程,是一个程序内部的一条执行路径

一个进程可以有多个线程;

线程的五个状态
代码语言:javascript
复制
1. 新建状态(New)         : 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
2. 就绪状态(Runnable)	 : 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
3. 运行状态(Running) 	 : 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4. 阻塞状态(Blocked)  	 : 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    	(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
    	(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    	(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()	等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead)    	 : 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
在这里插入图片描述
在这里插入图片描述
创建线程的三种方式

继承 Thread 类、实现 Runnable 接口、实现Callable接口

继承Thread类实现多线程案例模拟
代码语言:javascript
复制
//主线程
/*
*实现多线程,分别打印不同的数字
 */
public class Test {
    public static void main(String[] args) {
        //新建线程
        Thread test=new Test1();
        //启动线程
        test.start();
        //主线程执行 代码
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程打印:"+i);
        }
    }
}

//多线程继承Thread类
class Test1 extends Thread{

    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //Thread.currentThread().getName()获得当前线程的名字
            System.out.println(Thread.currentThread().getName()+"线程打印:"+i);
        }
    }
}


//执行main方法后,得到的结果(仅复制部分结果,可以自己试一下)
/*
主线程打印:0
Thread-0线程打印:0
主线程打印:1
Thread-0线程打印:1
主线程打印:2
主线程打印:3
主线程打印:4
*/
实现Runnable接口实现多线程案例模拟

注意:Runnable也需要通过Thread类启动线程

代码语言:javascript
复制
public class Test01 {
    //主线程
    public static void main(String[] args) {
        //新建线程
        Test2 test=new Test2();
        //启动线程:Runnable不能直接.start执行,也需要通过Thread类启动线程
        Thread t1=new Thread(test);
        t1.start();
        //主线程执行 代码
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印:"+i);
        }
    }
}

//多线程实现Runnable接口
class Test2 implements Runnable{

    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印:"+i);
        }
    }
}

//执行main方法后,得到的结果(仅复制部分结果,可以自己试一下)
/*
main线程打印:0
main线程打印:1
main线程打印:2
main线程打印:3
main线程打印:4
main线程打印:5
main线程打印:6
Thread-0线程打印:0
main线程打印:7
Thread-0线程打印:1
main线程打印:8
main线程打印:9
*/
实现Callable接口实现多线程

Callable和Runnable都是接口的形式实现多线程但是Callable可以有返回值,也可以抛出异常,与Thread和Runnable不同的是它并不是通过重写run()方法而是call()方法。

代码语言:javascript
复制
import java.util.concurrent.*;

public class Test02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Test3 t3=new Test3();
        ExecutorService executorService= Executors.newSingleThreadExecutor();
        Future submit = executorService.submit(t3);


        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印"+i);
        }
        System.out.println(submit.get());
    }
}

class Test3 implements Callable {

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程打印"+i);
        }

        return Thread.currentThread().getName()+"线程打印结束";
    }
}

//执行main方法后,得到的结果(仅复制部分结果,可以自己试一下)
/*
pool-1-thread-1线程打印99
main线程打印92
main线程打印93
main线程打印94
main线程打印95
main线程打印96
main线程打印97
main线程打印98
main线程打印99
pool-1-thread-1线程打印结束
*/
并发问题

多个线程操作同一个变量,存在线程不安全问题,数据紊乱

代码语言:javascript
复制
public class Test03 {
    public static void main(String[] args) {
        Test4 test4 = new Test4();
        new Thread(test4,"小刘").start();
        new Thread(test4,"小网").start();
        new Thread(test4,"小赵").start();
    }
}

class Test4 implements Runnable{

    private int tick = 20;

    @Override
    public void run() {
        while (true){
            if(tick<=0){
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
        }
    }
}
//运行结果:
/*
小网买了地19张票
小赵买了地18张票
小刘买了地20张票
小赵买了地17张票
小刘买了地16张票
小网买了地17张票
小网买了地15张票
小赵买了地14张票
小刘买了地13张票
小网买了地12张票
小赵买了地11张票
小刘买了地10张票
小赵买了地9张票
小刘买了地8张票
小网买了地9张票
小网买了地7张票
小刘买了地6张票
小赵买了地7张票
小赵买了地5张票
小网买了地4张票
小刘买了地5张票
小赵买了地3张票
小刘买了地2张票
小网买了地3张票
小网买了地1张票
小刘买了地0张票
小赵买了地1张票
*/
synchronized关键字
代码语言:javascript
复制
1. synchronized原理
在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。
当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如,synchronized(obj)就获取了“obj这个对象”的同步锁。
不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。
2. synchronized基本规则
第一条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
第二条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
第三条: 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
代码语言:javascript
复制
 @Override
    public synchronized void run() {
        while (true){
            if(tick<=0){
                break;
            }
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

//通过synchronized修饰方法,或者synchronized代码块的方式,使线程安全
 @Override
    public void run() {
        while (true){
            if(tick<=0){
                break;
            }
            synchronized (this){
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

//打印结果
/*
小刘买了地20张票
小赵买了地19张票
小网买了地18张票
小刘买了地17张票
小网买了地16张票
小赵买了地15张票
小网买了地14张票
小赵买了地13张票
小刘买了地12张票
小赵买了地11张票
小刘买了地10张票
小网买了地9张票
小赵买了地8张票
小刘买了地7张票
小网买了地6张票
小刘买了地5张票
小网买了地4张票
小赵买了地3张票
小刘买了地2张票
小网买了地1张票
*/
//注意:如果使用synchronized修饰run方法,其他线程访问该方法时就会被阻塞,所以该实例会只有第一个线程买票(建议自己试一下)

/*
死锁

同步可以保证资源共享操作的正确性,但是过多同步也会产生问题。例如,现在张三想要李四的画,李四想要张三的书,张三对李四说“把你的画给我,我就给你书”,李四也对张三说“把你的书给我,我就给你画”两个人互相等对方先行动,就这么干等没有结果,这实际上就是死锁的概念。

所谓死锁,就是两个线程都在等待对方先完成,造成程序的停滞,一般程序的死锁都是在程序运行时出现的。
*/
线程常用方法
代码语言:javascript
复制
线程的强制运行
在线程操作中,可以使用 join() 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

线程的休眠
在程序中允许一个线程进行暂时的休眠,直接使用 Thread.sleep() 即可实现休眠。

中断线程
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。

线程的优先级
在 Java 的线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时,哪个线程的优先级高,哪个线程就有可能会先被执行。线程.setPriority(1~10) ;Thread类中有三个定义好的常量

线程的礼让
在线程操作中,也可以使用 yield() 方法将一个线程的操作暂时让给其他线程执行

守护线程

线程分为用户线程守护线程

虚拟机必须确保用户线程执行完毕才行,虚拟机不用等待守护线程执行完毕;

可以用来声明日志,监控信息等用途;

声明守护线程的方式:线程.setDaemon(boolean);true为守护线程,也就是说这个线程里面是死循环也不会报错,当用户线程执行完毕,他也会结束执行;

代码语言:javascript
复制
public class Test {
    public static void main(String[] args) {
        //新建线程
        Thread test=new Test1();
        //启动线程
        test.setDaemon(true);
        test.start();
        //主线程执行 代码
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程打印:"+i);
        }
    }
}

//多线程继承Thread类
class Test1 extends Thread{

    //重写run()方法
    @Override
    public void run() {

        while (true){
            System.out.println(Thread.currentThread().getName()+"线程打印:");
        }

    }
}

//部分结果
/*
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
主线程打印:94
主线程打印:95
主线程打印:96
主线程打印:97
主线程打印:98
主线程打印:99
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:
Thread-0线程打印:

Process finished with exit code 0(运行结束)
*/

补充(ArrayList也是线程不安全的)可以网上了解一下

Lock(锁)

synchronized是隐式的同步锁;

还有一种显式的锁Lock对象锁;

ReentrantLock(可重入锁)类实现了Lock,可以显式加锁,释放锁;

代码语言:javascript
复制
public class Test03 {
    public static void main(String[] args) {
        Test4 test4 = new Test4();
        new Thread(test4,"小刘").start();
        new Thread(test4,"小网").start();
        new Thread(test4,"小赵").start();
    }
}

class Test4 implements Runnable{

    private volatile int tick = 10;

    private final ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while (true){
                if(tick<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }
}

//未加锁执行结果
/*
小网买了地10张票
小刘买了地8张票
小网买了地8张票
小赵买了地7张票
小赵买了地6张票
小网买了地4张票
小刘买了地5张票
小赵买了地2张票
小刘买了地1张票
小网买了地3张票
*/

//加锁后代码
class Test4 implements Runnable{

    private volatile int tick = 10;

    private final ReentrantLock lock=new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                //加锁
                lock.lock();
                if(tick<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"买了地"+tick--+"张票");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
}
//再执行
/*
小刘买了地10张票
小刘买了地9张票
小刘买了地8张票
小刘买了地7张票
小刘买了地6张票
小刘买了地5张票
小网买了地4张票
小网买了地3张票
小网买了地2张票
小网买了地1张票

*/
生产者消费者问题
代码语言:javascript
复制
生产者消费者问题是多线程的一个经典问题,描述成一块缓冲区作储存箱,生产者可以将产品放入储存箱,消费者则可以从储存箱中取走产品。

解决生产者/消费者问题的方法
采用某种机制保护生产者和消费者之间的同步
wait() / notify()方法(最高效)
wait() / nofity()方法是基类Object的两个方法:

wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
线程池

ExecutorService线程池接口

Executors工具类,线程池的工厂类

代码语言:javascript
复制
public class TestPool {
    public static void main(String[] args) {
        //创建服务,创建线程池;参数是线程池大小
        ExecutorService service= Executors.newFixedThreadPool(10);

        //启动线程
        service.execute(new Test5());
        service.execute(new Test5());
        service.execute(new Test5());
        service.execute(new Test5());

        //关闭连接
        service.shutdown();
    }
}

class Test5 implements Runnable{

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

//执行结果
/*
pool-1-thread-2打印了
pool-1-thread-4打印了
pool-1-thread-1打印了
pool-1-thread-3打印了
*/
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • java多线程
    • 进程与线程
      • 线程的五个状态
        • 创建线程的三种方式
          • Lock(锁)
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档