Java多线程

简介: Java多线程

进程与线程

进程(Process):进程是程序的一次动态执行过程,它经历了从代码加载、执行、到执行完毕的一个完整过程;同时也是并发执行的程序在执行过程中分配和管理资源的基本单位,竞争计算机系统资源的基本单位。

线程(Thread):线程可以理解为进程中的执行的一段程序片段,是进程的一个执行单元,是进程内可调度实体,是比进程更小的独立运行的基本单位,线程也被称为轻量级进程。

一、多线程的实现

1.继承Thread类

  • 方式1:继承Thread类

    • 定义一个类MyThread继承Thread类
    • 在MyThread类中重写run0方法
    • 创建MyThread类的对象
    • 启动线程

案例分析:

package com.Thread;
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(i);
        }
    }
}


package com.Thread;
public class MyThreadTest {
    public static void main(String[] args) {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
//        t1.run();
//        t2.run();
        //start()导致此线程开始执行,java虚拟机调用此线程的run方法
        t1.start();
        t2.start();
    }
}

1.设置和获取线程名称

package com.Thread;
public class MyThread extends Thread {
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            //getName()获取线程名称
            System.out.println(getName()+":"+i);
        }
    }
}



package com.Thread;
public class MyThreadTest {
    public static void main(String[] args) {
//        MyThread t1=new MyThread();
//        MyThread t2=new MyThread();
       /* //设置线程名称(方式一)
        t1.setName("小马哥");
        t2.setName("小飞侠");*/
//        t1.run();
//        t2.run();
        //start()导致此线程开始执行,java虚拟机调用此线程的run方法
        
        //设置线程名称(方式二)
        MyThread t1=new MyThread("小马哥");
        MyThread t2=new MyThread("小飞侠");

        t1.start();
        t2.start();
    }
}

2.线程优先级

  • Thread类中设置和获取线程优先级的方法

    • public final int getPriority0):返回此线程的优先级
    • public final void setPriority(int newPriority):更改此线程的优先级

案例分析:

package com.priority;

public class MyPriority extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}


package com.priority;

public class MyPriorityTest {
    public static void main(String[] args) {
        MyPriority p1=new MyPriority();
        MyPriority p2=new MyPriority();
        MyPriority p3=new MyPriority();
        p1.setName("线程一");
        p2.setName("线程二");
        p3.setName("线程三");

//        System.out.println(Thread.NORM_PRIORITY);  //5
//        System.out.println(Thread.MAX_PRIORITY);  //10
//        System.out.println(Thread.MIN_PRIORITY);  //1

        //设置线程优先级
        p1.setPriority(1);
        p2.setPriority(7);
        p3.setPriority(10);  //线程等级越高获取cpu时间片的几率高

        p1.start();
        p2.start();
        p3.start();
    }
}

3.线程控制

方法名 说明
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
void join() 等待这个线程死亡
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出

案例分析:

package com.control;

public class ThreadSleep extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(getName()+":"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

package com.control;

public class ThreadSleepTest {
    public static void main(String[] args) {
        ThreadSleep s1=new ThreadSleep();
        ThreadSleep s2=new ThreadSleep();
        ThreadSleep s3=new ThreadSleep();
        s1.setName("小马哥");
        s2.setName("小飞侠");
        s3.setName("老六");
        s1.start();
        s2.start();
        s3.start();

    }
}
package com.control;

public class ThreadJoin extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}

package com.control;

public class ThreadJoinTest {
    public static void main(String[] args) {
        ThreadJoin j1=new ThreadJoin();
        ThreadJoin j2=new ThreadJoin();
        ThreadJoin j3=new ThreadJoin();
        j1.setName("老大");
        j2.setName("老二");
        j3.setName("老三");
        j2.start();
        //当j2执行完之后,j1,j3才开始执行
        try {
            j2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        j1.start();
        j3.start();
    }
}
package com.control;

public class ThreadDaemon extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}

package com.control;

public class ThreadDaemonTest {
    public static void main(String[] args) {
        ThreadDaemon d1=new ThreadDaemon();
        ThreadDaemon d2=new ThreadDaemon();

        d1.setName("张飞");
        d2.setName("关羽");
        //设置主线程,主线程执行完毕之后,守护线程也会很快的结束
        Thread.currentThread().setName("刘备");
        //设置守护线程
        d1.setDaemon(true);
        d2.setDaemon(true);

        d1.start();
        d2.start();

        for (int i = 0; i <10 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }

    }

}

4.线程的生命周期

在这里插入图片描述

2.实现Runnable接口的方式实现多线程

  • 方式2:实现Runnable接口

    • 定义一个类MyRunnable实现Runnable接口
    • 在MyRunnable类中重写run()方法
    • 创建MyRunnable类的对象
    • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
    • 启动线程
  • 多线程的实现方案有两种

    • 继承Thread类
    • 实现Runnable接口
  • 相比继承Thread类,实现Runnable接口的好处

    • 避免了Java单继承的局限性
    • 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好子的体现了面向对象的设计思想。

案例分析:

package com.Runnable;

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

package com.Runnable;

public class MyRunableTest {
    public static void main(String[] args) {
        MyRunnable m=new MyRunnable();
        Thread t1=new Thread(m,"小马哥");
        Thread t2=new Thread(m,"小飞侠");
        t1.start();
        t2.start();
    }
}

二、线程同步

  • 锁多条语句操作共享数据,可以使用同步代码块实现
  • 格式:
synchronized(任意对象){
     多条语句操作共享数据的代码
)
  • synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁

同步的好处和弊端

  • 好处:解决了多线程的数据安全问题
  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

案例分析:

package com.SellTicketTest;

public class SellTicket implements Runnable {
    private int ticket=100;
    private Object obj=new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售出第" + ticket + "张票");
                    ticket--;
                }
            }
        }
    }
}


package com.SellTicketTest;

public class SellTicketTest {
    public static void main(String[] args) {
        SellTicket ticket=new SellTicket();
        Thread t1=new Thread(ticket,"窗口一");
        Thread t2=new Thread(ticket,"窗口二");
        Thread t3=new Thread(ticket,"窗口三");
        t1.start();
        t2.start();
        t3.start();

    }
}

三、线程安全的类

/*
线程安全的类;
    StringBuffer
    Vector
    HashtabLe
    */
public c1ass ThreadTest {
    public static void main(string[] args){
         StringBuffer sb = new StringBuffer();
         StringBuilder sb2 = new StringBuilder();
         
         Vector<String> v = new Vector<String>();
         ArrayList<String> array = new ArrayList<string>();
        Hashtable<String,String> ht = new Hashtable<String,string>();
        HashNap<String,string> hm = new HashMap<String,string>();
        List<String> list = Collections.synchronizedList(new ArrayList<String>());
      }
 }

四、Lock锁

  • Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作Lock中提供了获得锁和释放锁的方法

    • void lock():获得锁
    • void unlock():释放锁
  • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法

    • ReentrantLock():创建一个ReentrantLock的实例

案例分析:

package com.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyLock implements Runnable {
    private int ticket=100;
    private Lock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true) {

            try {
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在售出第" + ticket + "张票");
                    ticket--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
}

package com.lock;
import com.SellTicketTest.SellTicket;
public class MyLockTest {
    public static void main(String[] args) {
        MyLock ticket=new MyLock();
        Thread t1=new Thread(ticket,"窗口一");
        Thread t2=new Thread(ticket,"窗口二");
        Thread t3=new Thread(ticket,"窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

五、生产者消费者模式

  • ava就提供了几个方法供我们使用,这几个方法在Object类中Object类的等待和唤醒方法:
方法名 说明
void wait() 导致当前线程等待,直到另一个线程调用该对象的notify)方法或notifyAll)方法
void notify() 唤醒正在等待对象监视器的单个线程
void notifyAll() 唤醒正在等待对象监视器的所有线程

案例分析:

 
class Box{
    private int milk;
    private boolean state=false;
    public synchronized void put(int milk)  {//同步代码块:执行这块代码后,所在线程加锁,不会被抢占使用权。
                                             //这时其他线程要执行,需要wait()该线程,notify()其他线程
        if(state) {  //有奶,不再继续放,put的线程暂停,等待get线程拿出奶
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //state=false,没有奶,生产者放入奶,这是第i瓶
        this.milk=milk;
        System.out.println("生产者放入第"+this.milk+"瓶奶");
        state=true;   //有了奶,奶箱不为空,修改奶箱状态
        notifyAll();  //唤醒其他所有线程(唤醒get线程,取出牛奶)
    }
    public synchronized void get()  {
        if(!state) {  //state=false,没有奶,消费者没法拿出奶,只能等待
            try {
                wait();  //消费者的get行为/线程开始等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //state=true,有奶,可以拿出,这是第i瓶奶
        System.out.println("消费者拿出第"+this.milk+"瓶奶");
        state=false; //拿出以后,box空,修改box状态
        notifyAll();  //唤醒其他所有线程(唤醒put线程,开始放入)
    }
}
 
class Producer implements Runnable{
    private Box b;
    public Producer(Box b) {
        this.b=b;
    }
    @Override
    public void run() {
        for(int i=1;i<11;i++) {
            b.put(i);
        }
    }
   
}
 
class Customer implements Runnable{
    private Box b;
    public Customer(Box b) {
        this.b=b;
    }
    @Override
    public void run() {
        while(true) {
            b.get();
        }
    }
}
 
public class Milk {
    public static void main(String[] args) {
        Box b=new Box();    //创建一个奶箱
        Producer p=new Producer(b);  //都用这个奶箱
        Customer c=new Customer(b);
        Thread t1=new Thread(p);    //producer在线程1中
        Thread t2=new Thread(c);    //customer在线程2中
        t1.start();
        t2.start();
        
    }
}
相关文章
|
9天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
11天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
11天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
11天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
35 3
|
11天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
93 2
|
19天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
46 6
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
28天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
28天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
50 3
|
29天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####