多线程Java开发

线程

线程与进程
  • 进程:正在运行的应用程序。

在某一个时间点上,cpu(单核)只能只能执行一个进程。比如我们现在听歌(网易云)那还我使用IDEA敲代码,你的感觉肯定是这个两个事情是同时进行。是因为CPU可以在多个进程间进行一个高速的切换,人耳跟眼睛,是感觉不出来,多进程的意义,提高CPU的利用率。

  • 线程:线程依赖于进程,进程开启后,会执行很多的任务,那么每个任务,我们就称之为线程。

word进程——比如有两个任务,一个任务,是进行文字的输入,一个任务是自动保存。文字输入和自动保存是不是在同时进行。线程他是具有随机性的,他会取抢占CPU的执行权。谁抢到,CPU就会在某一个时刻执行对应线程。

并行与并发
  • 并行:多个任务同时执行。
    img
  • 并发:多个任务交替执行,只是交替的间隔,很短,让你感觉在同时执行。
    img

多线程

多线程:是指从软件或者硬件上实现多个线程并发执行的技术,是一种并发的思想。
单线程开发中的main方法就属于一个线程,叫做主线程。当我们开启其他线程和主线程同时执行是,就是多线程的思想。

Thread类

Thread 类是 Java 的线程类,通过它可以开启一个线程。

  • 构造方法
方法功能
Thread()分配新的 Thread 对象
Thread(Runnable target)分配新的 Thread 对象
Thread(Runnable target, String name)分配新的 Thread 对象,并定义线程名
Thread(String name)分配新的 Thread 对象 ,并定义线程名
  • 成员方法
方法功能
static Thread currentThread()返回对当前正在执行的线程对象的引用
String getName()返回该线程的名称
int getPriority()返回线程的优先级
void interrupt()中断线程
static boolean interrupted()测试当前线程是否已经中断
boolean isDaemon()测试该线程是否为守护线程
static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
static void yield()暂停当前正在执行的线程对象,并执行其他线程
开启线程
  • 方式一: 使用Java提供的 Thread 类,来创建线程
  1. 定义一个类,继承Thread类
  2. .重写Thread类中的run方法
  3. 创建此类的对象
  4. 开启线程(开启线程并不是调用run()方法,而是调用start() 开启线程,由线程去调用run()去执行run方法里面的代码)
public class 方式一 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println(i);
        }
        System.out.println("线程执行了");
    }
}
  • 方式二:通过实现 Runnable 接口,来创建线程
  1. 创建一个类,实现Runnable接口,重写接口中的run方法
  2. 创建Thread类对象,将Runnable接口的子类对象传递进来
  3. 调用start()方法开启线程
public class 方式二 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println(i);
        }
        System.out.println("线程执行了");
    }
}
  • 方式三:通过实现 Callable 接口,来创建线程
  1. 创建一个类,实现Callable接口,重写接口中的call方法
  2. 创建FutureTask类对象,将Callable接口的子类对象传递进来
  3. 创建Thread类对象,将FutureTask类对象传递进来
  4. 调用start()方法开启线程
public class 方式三 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        Integer i = futureTask.get();
        System.out.println("1-100总和是"+i);
    }
}

class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println("线程执行了");
        int sum=0;
        for (int i = 1; i <= 100; i++) {
            sum+=i;
        }
        return sum;
    }
}

实现Callable接口开启线程,在线程执行完后可以获取一个结果并用FutrueTask对象调取get()方法获得。

线程安全问题

  • 案例:模拟某电影院目前正在上映贺岁大片,共有100张票,3个售票窗口同时售票
public class 多线程售票 {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread1 = new Thread(runnable, "窗口1");
        Thread thread2 = new Thread(runnable, "窗口2");
        Thread thread3 = new Thread(runnable, "窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
public class MyRunnable implements Runnable {
    private static int ticket=100;

    @Override
    public void run() {
        while (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"剩余"+(ticket--)+"张票");
        }
    }
}

输出结果:
在这里插入图片描述
会发现出现相同票数的情况,零票甚至负数票的情况。

  1. 相同票:是由于原子性所导致的 ticket- - 他不是一个原子性的操作 (原子性:不可再分割)。在此线程运行完成后,ticket - -还未操作完成另一线程就开始运行,就会导致多个线程共享的数据出现被污染。
  2. 零票或负数票:由于线程的随机性所导致。
条件

出现线程安全问题的条件(缺一不可):

  1. 要是多线程环境
  2. 多个线程在并发操作共享数据
  3. 有多条语句在操作这个共享数据
解决

所以我们要解决线程安全问题,就从安全问题发生的条件入手:
使用同步代码块来解决线程安全问题
同步代码块可以看做一块锁,在一个进程操作共享数据时,将其它进程锁住,知道一个进程操作完毕,再放下一个进程运行。

synchronized (锁对象){
       放置有可能出现线程安全问题代码
         }
public class MyRunnable implements Runnable {
    private static int ticket=100;
    Object object=new Object();
    @Override
    public void run() {
        synchronized (object) {
            while (ticket>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"剩余"+(ticket--)+"张票");
            }   
        }
    }
}

这时共享数据被污染的安全问题迎刃而解。

锁对象
  • 当我们使用同步代码块时,锁对象可为任意对象
    在这里插入图片描述
  • 当我们使用同步方法时,锁对象为this
    在这里插入图片描述
  • 当我们使用静态同步方法时,需要使用类锁,锁对象为当前类的字节码类型
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值