文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java多线程创建及线程安全详解

2024-04-02 19:55

关注

什么是线程

线程的状态

线程的生命周期 

多线程创建方法

继承Thread



class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(currentThread().getName()+"运行了");
    }
}
class Test{
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        System.out.println(Thread.currentThread().getName()+":运行了");
        myThread.start();
    }
}

实现Runable接口创建多线程



class MThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i<100;i++){
            if (i%2!=0){
                System.out.println(i);
            }
        }
    }
}
public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建实现类的对象
        MThread mThread = new MThread();
        //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread thread = new Thread(mThread);
        thread.start();
    }
}

Thread和Runable创建多线程对比

开发中:优先使用Runable
1.实现的方式没有类的单继承的局限性。
2.实现的方式跟适合处理多个线程有共享数据的情况。
联系:Thread类中也实现了Runable,两种方式都需要重写run()。

实现Callable接口


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


class MCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for(int i=0;i<100;i++){
            sum+=i;
        }
        return sum;
    }
}
public class CallableTest {
    public static void main(String[] args) {
        //执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果
        FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(new MCallable());
       
        new Thread(integerFutureTask).start();
        //接受线程运算后的结果
        Integer integer = null;
        try {
            integer = integerFutureTask.get();
            System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

与Runable相比,Callable功能更强大

相比run()方法可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果

使用线程池进行创建

线程池创建的好处

提高响应速度(减少了创建新线程的时间)

降低资源消耗(重复利用线程池中线程,不需要每次都创建)

便于线程管理:


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


class Thread1 implements Runnable{

    @Override
    public void run() {
        for (int i=1;i<30;i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService executorService= Executors.newFixedThreadPool(10);
        Thread1 threadPool = new Thread1();
        for (int i=0;i<5;i++){
            //为线程池分配任务
            executorService.submit(threadPool);
        }
        //关闭线程池
        executorService.shutdown();
    }
}

Thread中的常用方法

线程的优先级

线程的优先级等级

涉及的方法

说明

线程的同步

问题

多线程卖票

基于实现Runable的方式实现多线程买票


package demo2;


class Thread2 implements Runnable{

    private  int ticket=100;
    @Override
    public void run() {
        while (true){
            if (ticket>0) {
                System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        Thread2 thread2 = new Thread2();
        Thread t1 = new Thread(thread2);
        Thread t2 = new Thread(thread2);
        Thread t3 = new Thread(thread2);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

实现结果,存在重复的票

实现结果

如果在买票方法中加入sleep函数


 public void run() {
        while (true){
            if (ticket>0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                ticket--;
            }else {
                break;
            }
        }
    }

则运行结果可能会出现-1,表示也是不正常的

出现-1的结果

理想情况

卖票理想状态

极端情况

极端情况

在java中,我们通过同步机制,来解决线程的安全问题。

同步代码块


synchronized(同步监视器){
	//需要被同步的代码
}

说明

修改之后的代码:


package demo2;


class Thread2 implements Runnable{

    private  int ticket=100;

    Object object = new Object();
    @Override
    public void run() {
        while (true){
            synchronized(object) { //括号中的内容可以直接使用当前对象this去充当
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        Thread2 thread2 = new Thread2();
        Thread t1 = new Thread(thread2);
        Thread t2 = new Thread(thread2);
        Thread t3 = new Thread(thread2);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

结果

运行结果

继承Thread的方式,去使用同步代码块,需要将声明的锁对象设为statci,否则创建的对象的同步监视器不唯一,就无法实现。


package demo2;


class WindowsTest2 extends Thread{
    private static int ticket=100;
    private static   Object obj = new Object();

    @Override
    public void run() {
        while (true){
            synchronized (obj){ //这里不能使用this去充当,可以直接写一个Test.class   类也是对象
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName()+":买票,票号为:"+ticket);
                    ticket--;
                }else {
                    break;
                }
            }
        }
    }
}
public class  Test2{
    public static void main(String[] args) {
        WindowsTest2 w1 = new WindowsTest2();
        WindowsTest2 w2 = new WindowsTest2();
        WindowsTest2 w3 = new WindowsTest2();
        w1.setName("窗口一");
        w2.setName("窗口二");
        w3.setName("窗口三");
        w1.start();
        w2.start();
        w3.start();
    }
}

同步方法

如果操作共享数据的代码完整的声明在一个方法中,可以将此方法声明为同步的。


通过实现Runable的方式实现同步方法。


package demo2;


class Thread3 implements Runnable {

    private int ticket = 100;


    @Override
    public void run() {
        while (true) {
            show();
        }

    }
    private synchronized void show(){
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);
            ticket--;
        }
    }
}

public class Test3 {
    public static void main(String[] args) {
        Thread3 thread3 = new Thread3();
        Thread t1 = new Thread(thread3);
        Thread t2 = new Thread(thread3);
        Thread t3 = new Thread(thread3);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

通过实现继承Thread的方式实现同步方法。使用的同步监视器是this,则不唯一,就会报错。所以将该方法定义为static。当前的同步换时期就变成Test4.class了


package demo2;


class WindowsTest4 extends Thread{
    private static int ticket=100;
    private static   Object obj = new Object();

    @Override
    public void run() {
        while (true){
            show();
        }

    }
    public static synchronized void show(){//同步监视器不是this了,而是当前的类
//    public synchronized void show(){//同步监视器是this  ,t1,t2,t3
        if (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":买票,票号为:"+ticket);
            ticket--;
        }
    }
}
public class  Test4{
    public static void main(String[] args) {
        WindowsTest4 w1 = new WindowsTest4();
        WindowsTest4 w2 = new WindowsTest4();
        WindowsTest4 w3 = new WindowsTest4();
        w1.setName("窗口一");
        w2.setName("窗口二");
        w3.setName("窗口三");
        w1.start();
        w2.start();
        w3.start();
    }
}

总结

Lock锁解决线程安全问题

synchronize与lock的异同

相同

不同

建议优先使用顺序

Lock------>同步代码块(已经进入了方法体,分配了相应资源)---->同步方法(在方法体之外)


package demo2;

import java.util.concurrent.locks.ReentrantLock;


class Lock1 implements Runnable{
    private int ticket=50;

    //1.实例化
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try {
                //2.调用lock锁定方法
                lock.lock();
                if (ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket);
                    ticket--;
                }else{
                    break;
                }
            } finally {
                //3.调用解锁方法
                lock.unlock();
            }
        }
    }
}
public class LockTest1 {
    public static void main(String[] args) {
        Lock1 lock1 = new Lock1();
        Thread t1 = new Thread(lock1);
        Thread t2 = new Thread(lock1);
        Thread t3 = new Thread(lock1);
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");
        t1.start();
        t2.start();
        t3.start();

    }
}

到此这篇关于java多线程创建及线程安全的文章就介绍到这了,更多相关java多线程创建内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯