多线程简介

一、进程和线程


1.1 进程

进程:程序是静止的,只有真正运行时的程序,才被称为进程。

1.1.1进程的状态

1) 就绪状态(Ready)
2) 运行状态(Running)
3) 阻塞状态(Blocked)

image-20230303143757643
1.2 线程

线程:又称轻量级进程(Light Weight Process)。

线程: 是进程中的一个执行路径, 共享一个内存空间, 线程之间可以自由切换, 并发执行, 一个进程最少有一个线程(单线程程序)

**并行:**两个任务同时运行(多CPU)

**并发:**两个任务同时请求运行,处理器只接受一个任务,两个任务轮流运行。

Java中创建线程主要有两种方式:

  • 继承Thread类。
  • 实现Runnable接口。
package ThreadDemo;

/**
 *
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();         //启动线程,

        MyRunnable mr = new MyRunnable();
        Thread t2 = new Thread(mr);
        t2.start();
    }
}

/**
 * 实现线程的方法1
 * 继承Thread
+ 编写类、继承Thread。
+ 重写run方法。
+ 创建线程对象。
+ 调用start方法启动线程。
 */
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+"-"+i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


/**
 * 实现线程的方法1
 * 实现Runnable接口
+ 编写类实现Runnable接口、并实现run方法。
+ 创建Runnable实现类对象。
+ 创建线程对象,传递实现类对象。
+ 启动线程。
 */
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+"-"+i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
进程和线程区别
  • 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
  • 一个程序运行后至少有一个进程。
  • 一个进程可以包含多个线程,但是至少需要有一个线程。
  • 进程间不能共享数据段地址,但同进程的线程之间可以。
1.3 线程休眠
public static void sleep(long millis)  

使当前执行的线程暂停执行,参数为毫秒,释放CPU时间片。

1.4 join与中断线程

public final void join() throws InterruptedException

等待这个线程死亡。 即先将时间片分给此线程,指导线程执行完毕后释放时间片资源。

public void interrupt()
中断这个线程。除非当前线程中断自身, 这是始终允许的 。

public static boolean interrupted()
测试当前线程是否中断。 该方法可以清除线程的中断状态 。

package ThreadDemo;

/**
 * join使线程先执行完毕后再释放资源
 *
 * 中断线程
 * 1、使用interrupt方法中断线程,设置一个中断状态,会被其他异常处理清除标记
 * 2、自定义标记(推荐)
 */
public class ThreadDemo2 {

    public static void main(String[] args) {
        MyRunnable2 mr2 = new MyRunnable2();
        Thread t = new Thread(mr2);
//        t.start();

        MyRunnable3 mr3 = new MyRunnable3();
        Thread t2 = new Thread(mr3);
        t2.start();

        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"--"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i==20){
//                try {
//                    t.join();   //先让线程t执行完毕
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                t.interrupt();      //中断线程,只做中断标记
                mr3.flag = false;
            }
        }
    }
}

class MyRunnable2 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            if (Thread.interrupted()){  //判断线程是否被中断,会把中断状态清除
                break;
            }
            System.out.println(Thread.currentThread().getName()+"--"+i);
            try {
                Thread.sleep(300);  //自己就会清楚中断标记
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt(); //添加打中断标记
            }
        }
    }
}

class MyRunnable3 implements Runnable{
    public boolean flag = true;     //自定义的中断标记
    public MyRunnable3(){       //构造方法,标记为true
        flag=true;
    }
    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println(Thread.currentThread().getName()+"线程==="+(i++));
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
1.5守护线程与yield

public final void setDaemon(boolean on)
将此线程标记为daemon线程或用户线程。 当运行的唯一线程都是守护进程线程时, Java虚拟机将退出。没有用户线程时,虚拟机开始退出。
public final boolean isDaemon()
测试这个线程是否是守护线程。

常见方法
方法名说明
public static void sleep(long millis)当前线程主动休眠 millis 毫秒。
public static void yield()当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。
public final void join()允许其他线程加入到当前线程中。
public void setPriority(int)线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。
public void setDaemon(boolean)设置为守护线程线程有两类:用户线程(前台线程)、守护线程(后台线程)
1.6 线程同步
线程同步

解决数据共享问题, 必须使用同步, 所谓同步就是指 多个线程在同一个时间段内只能有一个线程执行指定代码,其他线程要等待此线程完成之后才可以继续执行

线程进行同步, 有以下三种方法:
(1) 同步代码块
synchronized(要同步的对象){
要同步的操作 ;
}

synchronized(临界资源对象){ //对临界资源对象加锁
//代码(原子操作)

}

(2) 同步方法
public synchronized void method(){
要同步的操作 ;
}
(3) Lock(ReentrantLock)

package ThreadDemo;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程共享数据时,会发生线程不安全的情况
 * 因此必须使用同步
 */
public class ThreadDemo4 {

    public static void main(String[] args) {
        MyRunnable5 mr5 = new MyRunnable5();
        Thread t1 = new Thread(mr5);
        Thread t2 = new Thread(mr5);
        t1.start();
        t2.start();
    }
}

class MyRunnable5 implements Runnable{

    private int ticket = 10;
    private Object obj = new Object();  //线程对象
    @Override
    public void run() {
        for (int i = 0; i < 300; i++) {

//                synchronized (obj){     //需要同步的对象,将对象加锁
//                    ticket--;
//                    try {
//                        Thread.sleep(1000); //让出时间片,但是不会让出锁
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    System.out.println("购买的票已剩余:"+ticket+"张");
//                }
                method();
            }
        }


        //同步方法,同步到对象是当前对象(this)
        private synchronized void method() {
            if (ticket > 0) {
                ticket--;
                try {
                    Thread.sleep(1000); //让出时间片,但是不会让出锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("购买的票已剩余:" + ticket + "张");
            }
        }


        ReentrantLock lock = new ReentrantLock();   //互斥锁
        private void method2() {
            lock.lock();    //锁
            if (ticket > 0) {
                ticket--;
                try {
                    Thread.sleep(1000); //让出时间片,但是不会让出锁
                } catch (InterruptedException e) {
                    e.printStackTrace();

                }
                System.out.println("购买的票已剩余:" + ticket + "张");
                lock.unlock();  //释放锁
            }
        }
}

JDK中线程安全的类:

  • StringBuffer
  • Vector
  • Hashtable
    以上类中的公开方法,均为synchonized修饰的同步方法。
1.7 死锁

过多的同步有可能出现死锁, 死锁的操作一般是在程序运行的时候才有可能出现 。

在一个同步方法中调用另一个对象的同步方法,可能产生死锁。

package ThreadDemo;

public class ThreadDemo5 {

    public static void main(String[] args) {
       new DeadThread();
    }
}

class Customer{
    public synchronized void say(Water w){
        System.out.println("顾客:先吃饭后买单!");
        w.doService();
    }

    public synchronized void doService(){
        System.out.println("好的,买完单在吃饭");
    }
}

class Water{
    public synchronized void say(Customer c){
        System.out.println("服务员:先买单后吃饭!");
        c.doService();
    }

    public synchronized void doService(){
        System.out.println("好的,买吃饭在买单");
    }
}

//死锁线程
class DeadThread implements Runnable{
    Customer c = new Customer();
    Water w = new Water();
    public DeadThread(){    //构造方法
        new Thread(this).start();   //加了一个线程 理解不了
        w.say(c);
    }

    @Override
    public void run() {
        c.say(w);
    }
}
1.8 线程的生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HRCFIJw2-1678192311975)(C:\Users\asus\AppData\Roaming\Typora\typora-user-images\image-20230305103402000.png)]

1.9 线程池

线程池是预先创建线程的一种技术。 线程池在还没有任务到来之前, 创建一定数量的线程,放入空闲队列中, 然后对这些资源进行复用。 减少频繁的创建和销毁对象。

java.util.concurrent 包: 并发编程中很常用的实用工具类
Executor 接口:
执行已提交的 Runnable 任务的对象。
ExecutorService 接口:
Executor 提供了管理终止的方法, 以及可为跟踪一个或多个异步任务执行状况而生成Future 的方法。
Executors 类:
此包中所定义的Executor、 ExecutorService等的工厂和实用方法。

方法名描述
newFixedThreadPool(int nThreads)获取固定数量的线程池。参数:指定线程池中线程的数量。
newCachedThreadPool()获得动态数量的线程池,如不够则创建新的,无上限。
newSingleThreadExecutor()创建单个线程的线程池,只有一个线程。
newScheduledThreadPool()创建固定大小的线程池,可以延迟或定时执行任务。
package ThreadDemo;

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

/**
 * JDK1.5线程池
 */
public class ThreadDemo6 {

    public static void main(String[] args) {
        //创建线程池4种方法
        //1.创建单线程的线程池
//        ExecutorService es = Executors.newSingleThreadExecutor();

        //2.创建一个固定大小的线程池
//        ExecutorService es = Executors.newFixedThreadPool(2);

        //3.创建一个可缓存大小的线程池
//        ExecutorService es = Executors.newCachedThreadPool();

        //4.创建一个大小无限制的线程池
        ScheduledExecutorService es = Executors.newScheduledThreadPool(3);


        es.execute(new MyRunnable6());
        es.execute(new MyRunnable6());
        es.schedule(new MyRunnable6(),300, TimeUnit.MILLISECONDS);

        es.shutdown();
    }
}

class MyRunnable6 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"run--"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
le(new MyRunnable6(),300, TimeUnit.MILLISECONDS);

        es.shutdown();
    }
}

class MyRunnable6 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"run--"+i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值