# Java 线程和线程池的使用


线程安全和线程不安全
  • 线程安全:在Java的线程中进行加锁操作,当一个线程访问该资源的时候只能等到加锁的线程使用完资源后别的线程才能使用该线程。不会出现数据不一致或者产生数据污染等问题。
  • 线程不安全:对线程没有进行加锁操作有可能多个线程对数据操作,产生数据的污染等问题。

java中常见的线程安全和非安全的示例
  • ArrayList是非线程安全的、Vector是线程安全的 。
  • HashMap是非线程安全的、HashTable是线程安全的。
  • StringBuilder是非线程安全的、StringBuffer是线程安全的。
  • 线程不安全如下所示:代码运行结果如下
/**
 * 线程不安全
 * */
public class ThreadRunnable {

    private static final Logger logger = LoggerFactory.getLogger(ThreadRunnable.class);

    /**
     * 相同代码公用一个资源
     *
     * @param args
     */

    public static void main(String[] args) {
        int num = 10;
        ByTicketsRunnable byTicketsRunnable = new ByTicketsRunnable(num);
        for (int i = 0; i < 11; i++) {
            Thread thread = new Thread(byTicketsRunnable);
            thread.start();
            if (Thread.holdsLock(Thread.currentThread())) {
                logger.info("当前线程持有对象监视器!");
            }
        }
    }
}

class ByTicketsRunnable implements Runnable {

    private static final Logger logger = LoggerFactory.getLogger(ByTicketsRunnable.class);


    private int num;

    public ByTicketsRunnable(int num) {
        this.num = num;
    }

    @Override
    public void run() {
        synchronized (this) {
            if (num > 0) {
                num--;
                logger.info("Thread {} 买到一张票 还剩:{} 张票", Thread.currentThread().getId(), num);
            } else {
                logger.info("Thread {} 没有抢到票 还剩:{} 张票", Thread.currentThread().getId(), num);
            }
        }
    }

}

在这里插入图片描述

实现线程安全
  • synchronized锁修饰后当方法抛出异常或者遇到return时候都会释放锁。
public class LockTest1 {
    //单例对象
    private static Test1 test = null;
    //静态的方法
    public static synchronized Test1 getTest1(){
        if(test==null){
            test=new Test1();
        }
        return test;
    }
}
class Test1 {
}
  • ReentrabtLock:支持可重入的锁

Java中如何保证线程的安全

通过合理的时间调度,避免共享资源的存取冲突。设计一个规则保证一个计算工作只交给一个线程去完成,而不是把一个计算工作交个多个线程去实现。

线程的基本状态以及状态之间的关系
  • Running:运行状态
  • Runnable:就绪状态(万事具备只歉Cpu)
  • Blocked:表示阻塞状态(调用wait方法进入等到池子,调用sleep方法等待休眠或者其他进程结束,或是发生I/O中断)。
举例说明同步和异步
  • 如果系统中存在临界资源、例如正在写的数据被另一个线程读到,那么这些数据就必须进行同步存取。
  • 当应用程序在对象的调用上花了很长时间来执行,但是不希望让程序等待方法的返回。
实现线程安全
private int j = 0;
public static void main(String[] args) {
    MultiThreadTest1 multiThreadTest1 = new MultiThreadTest1();
    mt1 a = multiThreadTest1.new mt1();
    mt2 b = multiThreadTest1.new mt2();
    mt3 c = multiThreadTest1.new mt3();
    mt4 d = multiThreadTest1.new mt4();
    a.start();
    b.start();
    c.start();
    d.start();
}
private synchronized void add() {
    j++;
    System.out.println("当前的线程为:" + Thread.currentThread().getName() + "=====" + "add:" + j);
}
private synchronized void del() {
    j--;
    System.out.println("当前的线程为:" + Thread.currentThread().getName() + "=====" + "del:" + j);
}
class mt1 extends Thread {
    @Override
    public void run() {
        System.out.println("我是线程1");
        for (int i = 0; i <= 100; i++) {
            add();
        }
    }
}
class mt3 extends Thread {
        @Override
        public void run() {
            System.out.println("我是线程3");
            for (int i = 0; i <= 100; i++) {
                del();
            }
        }
 }
sleep()、wait()、notify()方法
  • sleep():是Thread的方法。导致此线程暂停执行指定时间,把执行机会让给别的线程到时候自动恢复调用sleep()不会释放对象锁。
  • wait():是Object的方法。次对象调用wait()方法会释放对象锁。
  • notify():本线程才进入线程池准备获得对象锁进入运行状态。
Java中的线程池类型
  • newFixedThreadPool(n):创建一个指定工作线程数量的线程池,如果线程数量达到初始化大小,则将提交的任务保存到池队列中。提高效率节省开销,不会释放空闲资源。
  • newCachedThreadPool():缓存线程池,可以灵活收回空闲线程,若无可回收则创建新的。默认为1分钟。
  • newSingThreadExcutor():只创建唯一的工作线程来执行任务,保证线程按照指定的书序执行,保证顺序的执行任务。
  • newScheduledThreadPool(n):支持定时及周期性任务执行。
/**
  * 线程池的种类
  * */
@Test
public void test1(){
    //缓冲线程池
    ExecutorService executorService=Executors.newCachedThreadPool();
    //指定工作量线程池子
    ExecutorService executorService1=Executors.newFixedThreadPool(3);
    //周期性任务线程池
    ExecutorService executorService2=Executors.newScheduledThreadPool(3);
    //单任务线程池子
    ExecutorService executorService3=Executors.newSingleThreadExecutor();
}
java锁
  • 乐观锁:读多写少。更新操作的时候上锁。
  • 悲观锁:写多杜少。读写数据都会上锁。
  • 自旋锁:如果持有锁的线程能在短时间内释放锁,那么等待的线程不需要阻塞挂起,他们只需要等一等,等持有锁的线程释放锁后立即获取锁。避免线程和内核的切换之间的消耗
  • synchronized同步锁:是一种独占锁,同时属于可重入锁。
Synchronized和Lock
  • Synchronized是java的关键字,当它修饰一个方法或者代码块时,能保证同一时刻只有一个线程运行改代码块。发生异常时候自动释放占有的锁,不会发生死锁现象
  • Lock在发生异常时候如果没有通过unlock()去释放锁则很可能造成死锁现象。使用Lock需要在finally中释放锁。Lock可以让等待的线程响应中断。使用Synchronized只能是等待的线程一直等待下去。

集成Thread重写run()
public class TestExtendsThread {
     static class Test1ExtendsThread extends Thread {
        @Override
        public void run() {
            System.out.println("我是Test1ExtendsThread的线程!");
        }
    }
    public static void main(String[] args) {
       new Test1ExtendsThread().start();
    }
}

实现Runnable接口
public class TestImplementsRunnable {
    static class ThreadImplementsTunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("Hellow");
        }
    }

    public static void main(String[] args) {
        ThreadImplementsTunnable thread=new ThreadImplementsTunnable();
        thread.run();
    }
}

实现Callable接口
public class TestImplementsCallable {
    static class T1 implements Callable{

        @Override
        public Object call() throws Exception {
            System.out.println("Hellow");
            return null;
        }
    }

    public static void main(String[] args) {
        T1 t1=new T1();
        FutureTask futureTask=new FutureTask<>(t1);
        futureTask.run();
    }
}
  • 上面的三种方式:频繁的创建、销毁对象消耗性能;导致占用过多的资源。

线程池的使用
  • 线程池和数据库连接池有类似的功效,可以控制线程的数量。可以使线程的使用率提升。减少对象的创建和销毁,可以合理的使用线程资源,保证资源的使用效益最大。
线程池的四种类型以及使用方法
newFixedThreadPool
  • 创建一个指定工作线程数量的线程池,如果线程数量达到初始化大小,则将提交的任务保存到池队列中。提高效率节省开销,不会释放空闲资源。
public class FixThreadPool {
    public static void method_01() throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            Thread.sleep(1000);
            int index = i;
            executor.execute(() -> {
                try {
                    Thread.sleep(2 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "  " + index);
            });
        }
        executor.shutdown();
    }

    public static void main(String[] args) {
       try {
           method_01();
       }catch (Exception e){
           e.printStackTrace();
       }
    }
}

newCachedThreadPool
  • 缓存线程池,可以灵活收回空闲线程,若无可回收则创建新的。默认为1分钟。
public class CachedThreadPoolTest {

    public void method()  {
        try {
            this.doOne(5);
            this.doTwo(6);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void doTwo(int x)  throws Exception{
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < x; i++) {
            final int index = i;
            Thread.sleep(4000);
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "  " + index);
                }
            });
        }
    }
    private void doOne(int y) throws Exception {
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < y; i++) {
            final int index = i;
            Thread.sleep(4000);
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "  " + index);
                }
            });
        }
    }
    public static void main(String[] args) {
        CachedThreadPoolTest cachedThreadPoolTest=new CachedThreadPoolTest();
        cachedThreadPoolTest.method();
    }
}

newScheduledThreadPool
  • 支持定时及周期性任务执行。
public class ScheduledThreadPoolTest {
    public static void method_02() {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                long start = new Date().getTime();
                System.out.println("scheduleAtFixedRate 开始执行时间:" + DateFormat.getTimeInstance().format(new Date()));
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long end = new Date().getTime();
                System.out.println("scheduleAtFixedRate 执行花费时间=" + (end - start) / 1000 + "m");
                System.out.println("scheduleAtFixedRate 执行完成时间:" + DateFormat.getTimeInstance().format(new Date()));
                System.out.println("======================================");
            }
        }, 1, 5, TimeUnit.SECONDS);
    }

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

newSingleThreadExecutor
  • 只创建唯一的工作线程来执行任务,保证线程按照指定的书序执行,保证顺序的执行任务。
public class SingleThreadExecutorTest {
    public static void method_04() {
        try {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 5; i++) {
                final int index = i;
                executor.execute(() -> {
                    try {
                        Thread.sleep(2 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "   " + index);
                });
            }
            executor.shutdown();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        method_04();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java中,线程是程序执行的基本单元,用于并发执行任务。每个线程都有自己的生命周期,包括创建、运行、阻塞和终止。线程的创建可以通过继承Thread类或实现Runnable接口来实现。 线程池则是Java中一种高效的线程管理机制,它预先创建一定数量的工作线程,并在需要执行任务时从线程池中获取线程进行处理,当任务完成后,线程会返回到线程池等待下一次调度,而不是立即结束。这样可以避免频繁地创建和销毁线程带来的开销,提高系统的性能和资源利用率。 以下是Java线程线程池的一些关键点: 1. **线程创建**: - **继承Thread类**:创建自定义线程类并重写run()方法。 - **实现Runnable接口**:创建Runnable接口的实现类,提供run()方法,然后用Thread构造函数创建Thread实例。 2. **线程状态**: - 新建(New):线程对象被创建但还未启动。 - 运行(Runnable):线程正在执行run()方法。 - 阻塞(Blocked):线程因某个条件而暂停,如I/O操作等待数据。 - 等待(Waiting):线程在调用wait()方法后,进入等待状态,直到被其他线程唤醒。 - 守护(Terminated):非守护线程完成或主线程结束,守护线程自动退出。 3. **线程池组件**: - ExecutorService:线程池的核心接口,提供了提交任务和控制线程的方法。 - ThreadPoolExecutor:实现了ExecutorService,包含核心线程数、最大线程数、任务队列等配置。 - ScheduledThreadPoolExecutor:支持定时和周期性任务。 4. **线程池的优势**: - **资源复用**:减少线程创建和销毁的开销。 - **线程管理和调度**:灵活设置线程数量、线程优先级和任务执行策略。 - **避免死锁**:由于任务有顺序地等待特定资源,减少了死锁的可能性。 - **可扩展性**:随着任务增加,线程池可以根据需要动态调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

全栈程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值