创建多线程四种解决方式 和 多线程安全问题 解决使用 Synchronized 锁 和 Lock锁

创建线程的两种方式

方式一:继承Thread类

  1. 定义子类继承Thread类。
  2. 子类中重写Thread类中的run方法。
  3. 创建Thread子类对象,即创建了线程对象。
  4. 调用线程对象start方法:启动线程,调用run方法。

方式二:实现Runnable接口

  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法。
  3. 通过Thread类含参构造器创建线程对象。
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

方式三:实现Callable接口
与使用Runnable相比, Callable功能更强大些
相比run()方法,可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果

方式四:使用线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,
对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完
放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交
通工具。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止

一:Synchronized 锁 的使用方法

Java对于多线程的安全问题提供了专业的解决方式:同步机制(给操作共享资源的代码加锁)
			1. 同步代码块:
		synchronized (对象){
		// 需要被同步的代码;
		}

		2. synchronized还可以放在方法声明中,表示整个方法为同步方法。
		例如:
			public synchronized void show (String name){.
			}

synchronized的锁是什么?

任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
同步方法的锁:静态方法(类名.class)、非静态方法(this)
同步代码块:自己指定,很多时候也是指定为this或类名.class

注意:

必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就
无法保证共享资源的安全
一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方
法共用同一把锁(this),同步代码块(指定需谨慎)

一 测试 实现Runnable接口

package com.example.test;

public class Ticket implements Runnable {

    private int tick =  100;

	
    @Override
    public void run() {

        while (true){
                if(tick > 0){
                    System.out.println(Thread.currentThread().getName()+"售出车票,车票号为:" +tick);
                    tick --;
                }
        }
    }

    public  static void  main(String[] args){
        Ticket ticket = new Ticket();

        Thread t1 =new Thread(ticket);
        Thread t2 =new Thread(ticket);
        Thread t3 =new Thread(ticket);
        
		t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }
}

效果: 出现超卖问题
在这里插入图片描述
解决方案:加锁

方案1:同步代码块

package com.example.test;

public class Ticket implements Runnable {

    private int tick =  100;
	 /**
     *
     *  同步代码块 
     *
     */
    @Override
    public void run() {

        while (true) {
                synchronized (this){  //this 当前对象
            if (tick > 0) {
                System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
                tick--;
              }
           }
        }
    }

    public  static void  main(String[] args){
        Ticket ticket = new Ticket();

        Thread t1 =new Thread(ticket);
        Thread t2 =new Thread(ticket);
        Thread t3 =new Thread(ticket);


        t1.setName("窗口1");  //线程1
        t2.setName("窗口2");  //线程2
        t3.setName("窗口3");  //线程3

        t1.start();
        t2.start();
        t3.start();

    }
}

效果:
在这里插入图片描述

方案2:同步方法

package com.example.test;

public class Ticket implements Runnable {

		private static int tick =  100;
	

    /**
     *
     *  同步方法
     *
     */

    @Override
    public void run() {

        while (true) {
            show();;
        }
    }

    private static synchronized void show(){
        if (tick > 0) {
            System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
            tick--;
        }
    }

    public  static void  main(String[] args){
        Ticket ticket = new Ticket();

        Thread t1 =new Thread(ticket);
        Thread t2 =new Thread(ticket);
        Thread t3 =new Thread(ticket);


        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }

效果:
在这里插入图片描述
二 测试 继承Thread 类

package com.example.test;

public class TicketTest extends Thread {

    private  int tick =  100;

    @Override
    public void run() {

        while (true) {
            if (tick > 0) {
                System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
                tick--;
              }
           }

    }

    public  static void  main(String[] args){
        TicketTest t1 = new TicketTest();
        TicketTest t2 = new TicketTest();
        TicketTest t3 = new TicketTest();


        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }


}

运行效果:出现超卖问题
在这里插入图片描述
解决方案:加锁

方案1:同步方法

package com.example.test;

public class TicketTest extends Thread {

    private static int tick =  100;

    @Override
    public void run() {

        while (true) {
            show();;
        }
    }

    private static synchronized void show(){
        if (tick > 0) {
            System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
            tick--;
        }
    }

    public  static void  main(String[] args){
        TicketTest t1 = new TicketTest();
        TicketTest t2 = new TicketTest();
        TicketTest t3 = new TicketTest();


        t1.setName("窗口1"); //线程1
        t2.setName("窗口2"); //线程2
        t3.setName("窗口3"); //线程3

        t1.start();
        t2.start();
        t3.start();

    }


}
package com.example.test;

import java.awt.*;

public class TicketTest extends Thread {

    private static int tick =  100;

    /**
     *
     *  同步代码块
     *
     */


   @Override
    public void run() {

        while (true) {
            synchronized (TicketTest.class){  //TicketTest.class 当前类.class 
            if (tick > 0) {
                System.out.println(Thread.currentThread().getName() + "售出车票,车票号为:" + tick);
                tick--;
              }
           }
        }
    }

  

    public  static void  main(String[] args){
        TicketTest t1 = new TicketTest();
        TicketTest t2 = new TicketTest();
        TicketTest t3 = new TicketTest();


        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }


}

效果:
在这里插入图片描述
二 Lock(锁)

从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同
步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的
工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象
加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和
内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以
显式加锁、释放锁。

public String class {
    private final ReentrantLock lock = new ReenTrantLock();
    public void m(){
        lock.lock(); //加锁 
        try{
            //保证线程安全的代码;
        }
        finally{
            lock.unlock(); //解锁
        }
    }
}

三 测试 实现 Callable接口

package com.example.test;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TicketTest2 implements Callable {

    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i=0 ;i <100 ;i++){
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName()+"---"+i);
                sum+=i;
            }

        }

        return sum;
    }

    public  static void  main(String[] args) throws ExecutionException, InterruptedException {
        TicketTest2 ticket = new TicketTest2();

        FutureTask futureTask = new FutureTask(ticket);

        futureTask.run();  //启动

        System.out.println(futureTask.get()); //获取sum值

    }
}

效果:
在这里插入图片描述
四 测试 使用线程池

package com.example.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.LinkedBlockingQueue;

/**
 * @des 线程池的简单实现(可扩展)
 */
public class ThreadPool {
    private static final Logger logger = LoggerFactory.getLogger(ThreadPool.class);
    private final int poolSize;
    private final LinkedBlockingQueue queue;
    private final PoolWorker[] runable;

    public ThreadPool(int poolSize) {
        this.poolSize = poolSize;
        queue = new LinkedBlockingQueue();
        runable = new PoolWorker[poolSize];
        for (int i = 0; i < poolSize; i++) {
            runable[i] = new PoolWorker();
            new Thread(runable[i], "pool-" + i).start();
        }
    }

    public void execute(Runnable task) {
        synchronized (queue) {
            queue.add(task);
            queue.notify();
        }
    }

    private class PoolWorker implements Runnable {
        @Override
        public void run() {
            Runnable task ;

            while (true) {
                synchronized (queue) {
                    while (queue.isEmpty()) {
                        try {
                            queue.wait();
                        } catch (Exception e) {
                            logger.info("exception in queue waiting :{}",e.getMessage());
                        }
                    }
                    task = (Runnable) queue.poll();
                }
                try {
                    task.run();
                } catch (RuntimeException e) {
                    logger.info("run exception : {}", e.getMessage());
                }

            }
        }
    }

}

class ThreadPoolMain {
    public static void main(String[] args) {
        ThreadPool pool = new ThreadPool(5);
        int MaxSize = 100;
        for (int i = 0; i < MaxSize; i++) {
            pool.execute(() -> System.out.println(Thread.currentThread()));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

效果: 线程池原文 https://cloud.tencent.com/developer/article/1494618
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

jq1223

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

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

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

打赏作者

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

抵扣说明:

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

余额充值