Java 基础知识六

多线程与并发编程

多进程概念


当前的操作系统都是多任务OS
每个独立执行的任务就是一个进程
OS将时间划分为多个时间片(时间很短)
每个时间片内将CPU分配给某一个任务,时间片结束CPU将自动回收,再分配给另外任务。从外部看,所有任务是同时在执行。但是在CPU上,任务是按照串行依次运行(单核CPU)如果是多核,多个进程任务可以并行

但是单个核上,多进程只能串行执行。

多进程的优点
可以同时运行多个任务
程序因IO堵塞时,可以释放CPU,让CPU为其他程序服务
-当系统有多个CPU时,可以为多个程序同时服务

多进程的缺点
-太笨重,不好管理
-太笨重,不好切换

多线程概念


个程序可以包括多个子任务,可串/并行
每个子任务可以称为一个线程
如果一个子任务阻塞,程序可以将CPU调度另外一个子任务进行工作。这样CPU还是保留在本程序中,而不是被调度到别的程序(进程)去。这样,提高本程序所获得CPU时间和利用率

多进程 vs 多线程-线程共享数据
-线程通讯更高效
-线程更轻量级,更容易切换-多个线程更容易管理

public class ProcessDemo1 {
    public static void main(String[] args) throws Exception {

        while (true) {
            int a = (int) (Math.random() * 100);
            System.out.println(" main thread is running " + a);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();

            }
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) throws Exception {

        new TestThread1().start();
        while(true){
            System.out.println("main thread is running");
            Thread.sleep(1000);
        }
    }
}
class TestThread1 extends Thread {
    public void run() {
        while (true){
            System.out.println("TestThread1 is running");
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Java的四个主要接口:

Clonable,用于对象克隆;Comparable,用于对象比较:

Serializable,用于对象序列化Runnable用于对象线程化。

Java 多线程创建


java.lang.Thread线程继承Thread类,实现run方法
java.lang.Runnable接口线程实现Runnable接口,实现run方法

Java多线程启动



-start方法,会自动以新进程调用run方法
-直接调用run方法,将变成串行执行-
-同一个线程,多次start会报错,只执行第一次start方法

-多个线程启动,其启动的先后顺序是随机的

-线程无需关闭,只要其run方法执行结束后,自动关闭

-main函数(线程)可能早于新线程结束,整个程序并不终止

-整个程序终止是等所有的线程都终止(包括main函数线程)


public class ThreadDemo3 {
    public static void main(String[] args) throws Exception {
        //new TestThread3().start();
        // Runnable对象必须放在一个Thread类中才能运行
        TestThread3 tt = new TestThread3();//创建TestThread类的一个实例
        Thread t = new Thread(tt);//创建一个Thread类的实例
        t.start();//使线程进入Runnable状态
        while (true) {
            System.out.println("main thread is running");
            try {
                Thread.sleep(1000);//1000毫秒
            } catch (InterruptedException e) {// ToD0 Auto-generated catch block
                e.printStackTrace();
            }

        }
    }
}

class TestThread3 implements Runnable//extends Thread
{
    //线程的代码段,当执行start()时,线程从此出开始执行
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + " is running");
            try {
                Thread.sleep(1000);//1000毫秒
            } catch (InterruptedException e) {// ToD0 Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

Java 多线程实现对比
Thread vs Runnable
-Thread占据了父类的名额,不如Runnable方便
-Thread 类实现Runnable
-Runnable启动时需要Thread类的支持
-Runnable更容易实现多线程中资源共享
·结论:建议实现Runnable接口来完成多线程

多线程信息共享

线程类
-通过继承Thread或实现Runnable
通过start方法,调用run方法,run方法工作-
线程run结束后,线程退出
粗粒度:子线程与子线程之间、和main线程之间缺乏交流
细粒度:线程之间有信息交流通讯
-通过共享变量达到信息共享
-JDK原生库暂不支持发送消息(类似MPI并行库直接发送消息)

通过共享变量在多个线程中共享消息
-static变量
-同一个Runnable类的成员变量

package org.example;

public class ThreadDemo2 {
    public static void main(String[] args) {
        new TestThread0().start();
        new TestThread0().start();
        new TestThread0().start();
        new TestThread0().start();
    }
}
class TestThread0 extends Thread {
    //private int tickets =100; //每个线程卖100张 没有共享
    private static int tickets=100; // static 变量时共享的 所有的线程共享
    public void run() {
        while (true){
            if (tickets >0){
                System.out.println(Thread.currentThread().getName()+ " is selling ticket "+tickets);
                tickets--;
            }else {
                break;
            }
        }
    }
}

这个例子会出现数据不一致情况

多线程信息共享问题
-工作缓存副本
-关键步骤缺乏加锁限制

i++,并非原子性操作

-读取主存i(正本)到工作缓存(副本)中

-每个CPU执行(副本)i+1操作

-CPU将结果写入到缓存(副本)中

-数据从工作缓存(副本)刷到主存(正本)中

变量副本问题的解决方法

-采用volatile 关键字修饰变量

-保证不同线程对共享变量操作时的可见性


public class ThreadDemo4 {
    public static void main(String[] args) throws  Exception {
        TestThread2 t= new TestThread2();
        t.start();
        Thread.sleep(1000);
        t.flag =false;
        System.out.println("main thread is exiting");
    }
}
class TestThread2 extends Thread {
   //boolean flag = true; //子线程不会停止
   volatile boolean flag = true;  //用 volatile 修饰的变量可以及时在各线程里面通知
    public void run() {
        int i=0;
        while (flag){
            i++;
        }
        System.out.println("test thread3 is exiting");
    }
}

关键步骤加锁限制
-互斥:某一个线程运行一个代码段(关键区),其他线程不能同时运行这个代码段
-同步:多个线程的运行,必须按照某一种规定的先后顺序来运行

-互斥是同步的一种特例
互斥的关键字是synchronized
-synchronized代码块/函数,只能一个线程进入

-synchronized加大性能负担,但是使用简便

package org.example;

public class ThreadDemo5 {
    public static void main(String[] args) throws Exception {
        TestThread5 t = new TestThread5();
        new Thread(t, "Thread-0").start();
        new Thread(t, "Thread-1").start();
        new Thread(t, "Thread-2").start();
        new Thread(t, "Thread-3").start();
    }
}

class TestThread5 implements Runnable {
    private volatile int tickets = 100;  //用 volatile 修饰的变量可以及时在各线程里面通知
    String str = new String("");
    public void run() {
        while (true) {
            synchronized (str) {//同步代码块  str 相当于一把锁  锁要加到对象上
                save();
            }
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            if (tickets <=0) {
                break;
            }
        }
    }

    public synchronized void save() {   // 19 行 synchronized  保留一处即可
        if (tickets>0){
            System.out.println(Thread.currentThread().getName()+" is selling ticket "+tickets--);
        }
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

larance

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值