【Java】创建线程的方式:继承Thread和实现Runnable接口(多线程安全问题)

继承Thread和实现Ruunable接口的区别

1.首先继承Thread是父类与子类的关系,实现Ruunable接口是实现类和接口的关系。

2.继承只能单继承,而接口可以多实现。(先继承后实现。)

3.如果一个类继承Thread,则不适合资源共享(成员变量加上static修饰也可以实现资源共享),但是实现了Runnable接口的话,则容易实现资源共享。

4.实现Runnable接口,增加程序的健壮性。代码可以被多个线程共享,代码和属性共享

继承Thread和实现Ruunable接口的特点

 继续Thread类

重写run方法。测试类时,创建多个线程,加上static修饰成员变量表示数据共享就是共同完成一个任务。

实现Runnable接口

重写run方法,并且将Runnable子类对象作为参数,传入Thread对象
创建一个实现类对象实现runnable接口,测试类时,只用创建一个实现类对象,可以创建多个线程共同完成任务,(不用static修饰成员变量)

多线程安全问题

当多个线程同时访问一个资源时就会出现数据冲突的问题,这就是多线程安全问题。出现在生活中有很多可能出现线程安全问题的例子(12306火车票,淘宝京东购物等等,但是这些大厂早已经完善了这些问题,技术也远远超越了我们所能接收的范围)

是分别对继承Thread类和实现Runnable接口的三种解决线程安全问题的方法

1-同步代码块

2-同步方法

3-锁机制

继承Thread类

一、继承Thread类- 线程测试类(测试类都是一致的,主要区别在于解决线程安全的方法)

ublic class BeanThreadTest {
	public static void main(String[] args) {
		/** 创建5个线程BeanThread对象表示窗口 */
		BeanThread t1 = new BeanThread("豆11");
		BeanThread t2 = new BeanThread("豆22");
		BeanThread t3 = new BeanThread("豆33");
		BeanThread t4 = new BeanThread("豆44");
		BeanThread t5 = new BeanThread("豆55");
		
		//调用start方法启动线程
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}

一、继承Thread类()-同步代码块

public class BeanThread extends Thread {
	// 1.声明一个成员变量用被每一个线程对象共享static修饰,
	private static int bean = 50;

	// 声明一个saleone方法表示买票功能
	private void saleOne() {
		Thread ct = Thread.currentThread();// 获取当前线程对象
		String name2 = ct.getName();// 获取当前线程对象名
		/**
		 * 线程同步方式一:同步代码块
		 * 基本语法结构
			synchronized (同步监听对象) {
				可能引发线程安全问题的代码
			}
		 */
		synchronized (BeanThread.class) {
			if (bean > 0) {
				System.out.println(name2+":抢到豆子:" + bean);
				bean--;
			}
		}	
	}
	/**
	 * 构造方法
	 * 无参构造方法和带参构造方法 
	 */
	public BeanThread() {
		super();
	}

	public BeanThread(String name) {
		super(name);
	}
	
	/**
	 * 重写run方法,方法中用while循环调用saleone方法
	 */
	@Override
	public void run() {
			while (bean > 0) {
				// 调用卖票方法saleone
				saleOne();
		}
	}
}

二、继承Thread类()-同步方法

public class BeanThread extends Thread {
	// 1.声明一个成员变量用被每一个线程对象共享static修饰,
	private static int bean = 100;
	/**
	 * 2. 同步方法(重点掌握)
			用synchronized关键字修饰方法即可,在修饰符位置,返回值前面
			如果方法是static修饰的:同步的是 :当前类.class 
			如果方法是非static修饰的:同步的是: this 
			
			如果继承Thread的方式,同步方法的话,就需要将方法改为static修饰,所以说,一般我们不用同步方法
			一般建议继承Thread用同步代码块或者锁机制
	 */
	// 声明一个robBean方法表示抢豆子功能
	private static synchronized void robBean() {// 同步方法,如果方法是类方法,则同步对象默认是:当前类.class
	//private  synchronized void robBean() {//同步方法,如果方法是实例方法,则同步对象默认是:this
		Thread ct = Thread.currentThread();// 获取当前线程对象
		String name2 = ct.getName();// 获取当前线程对象名	
			if (bean > 0) {
				System.out.println(name2+":抢到豆子:" + bean);
				bean--;
			}
	}
	/**
	 * 构造方法
	 * 无参构造方法和带参构造方法 
	 */
	public BeanThread() {
		super();
	}

	public BeanThread(String name) {
		super(name);
	}
	
	/**
	 * 重写run方法,方法中用while循环调用robBean方法
	 */
	@Override
	public void run() {
			while (bean > 0) {
				// 调用卖票方法robBean
				robBean();
		}
	}
}

 三、继承Thread类()-锁机制

/**
 * 线程同步方式三:锁机制
 * 启动线程方法一:继承Thread类
 * 第二种:(重点掌握)
					锁机制: Lock接口的实现类ReentrantLock【可重入互斥锁】
						构造方法:
							1. ReentrantLock() 创建一个 ReentrantLock的实例。   不要公平机制。效率高
							2. ReentrantLock(boolean fair) 根据给定的公平政策创建一个 ReentrantLock的实例。 
								 理论上获取锁的几率是相同的
						 class X {
						   private static final ReentrantLock lock = new ReentrantLock();
						
						   public void m() { 
						     lock.lock();  // 上锁
						     try {
						       	// 有线程安全问题的代码
						     } finally {
						       lock.unlock();// 释放锁 不释放锁的话,资源就一直被占用着
						     }
						   }
						 }
 */
public class BeanThread extends Thread{
	// static定义表示所有线程进行共享
	private static int bean = 100;
	
	//private final ReentrantLock lock = new ReentrantLock(); //创建对象的方式创建
	//要对lock锁进行static修饰共享一把锁,当线程调用过后,其他就不能在获取了
	private final static ReentrantLock lock = new ReentrantLock(); //RentrantLock重入锁
	/**
	 * 构造方法
	 */
	public BeanThread() {
		super();
		// TODO Auto-generated constructor stub
	}

	public BeanThread(String name) {
		super(name);
	}
	/**
	 * 定义一个方法进行抢豆子
	 */
	private void robbean() {
		Thread currentThread = Thread.currentThread(); // 获取当前线程
		String name2 = currentThread.getName(); // 获取当前线程的名字
		lock.lock(); //上锁  最小范围机制(保证运行效率)
		try {
			if (bean>0) {			
				System.out.println(name2+"抢到豆子:"+bean);
				bean--;	
			}	
		} finally { // finally修饰表示必须执行的程序
			lock.unlock();
		}
	}
	@Override
	public void run() {
		while(bean>0){
			robbean();
		}
	}
}

实现Runnable接口

二、实现Runnable接口- 线程测试类(测试类都是一致的,主要区别在于解决线程安全的方法

public class BeanImplTest {
	public static void main(String[] args) {
		/** 创建1个线程BeanImpl对象 */
		BeanImpl t1 = new BeanImpl();
		/**
		 * 因为Runnable接口没有start方法
		 * 需要创建线程对象将参数Runnable实现类对象传进去
		 */
		Thread t11 = new Thread(t1,"豆12");
		Thread t22 = new Thread(t1,"豆23");
		Thread t33 = new Thread(t1,"豆23");
		Thread t44 = new Thread(t1,"豆45");
		Thread t55 = new Thread(t1,"豆56");
			
		//调用start方法启动线程
		t11.start();
		t22.start();
		t33.start();
		t44.start();
		t55.start();
	}
}

一、实现Runnable接口-同步代码块

public class BeanThreadImpl implements Runnable {
	// 1.声明一个成员变量用被每一个线程对象共享static修饰,
	private static int bean = 200;
	
	//private final ReentrantLock lock = new ReentrantLock(); //创建对象的方式创建
	//要对lock锁进行static修饰共享一把锁,当线程调用过后,其他就不能在获取了
	private final static ReentrantLock lock = new ReentrantLock();
	String name;
	/**
	 * 构造方法 无参构造方法和带参构造方法
	 */
	public BeanThreadImpl() {
		super();
	}

	public BeanThreadImpl(String name) {
		this.name = name;
	}

	private void saleOne() {
	
		try { // 有线程安全的代码
			lock.lock();
			if (bean > 0) {
				System.out.println(name + ":当前豆子个数:" + bean);
				bean--;
			}	
		} finally {
			lock.unlock();
		}		
	}

	/**
	 * 重写run方法,方法中用while循环调用saleone方法
	 */
	@Override
	public void run() {
		while (bean > 0) {
			saleOne();
		}
	}
}

二、实现Runnable接口-同步方法

public class BeanImpl implements Runnable{
	/**
	 * 声明一个static修饰的成员变量被每个线程共享
	 */
	private static int bean = 100; 
	// 成员变量
	 String name;	
	/**
	 * 构造方法
	 * 定义一个带参构造方法
	 */
	public BeanImpl() {}
	
	public BeanImpl(String name) {
		this.name = name;
	}		
	//定义一个方法实现抢豆子功能
	private static synchronized void robBean() {
				/**
				 * 不加if判断,线程1买最后一个豆子,线程2和线程3也进来了,
				 * 在外面等待,但是他们不知道线程1卖的最后一个豆子,就会去执行抢豆子功能
				 * 再加一个if判断,从而线程2,3进来过后发现豆子没了就不会进行抢豆子了
				 */
				if (bean>0) {
					System.out.println(Thread.currentThread().getName()+"豆子;"+bean);
					bean--; // 每执行一次豆子个数就减1
				}	
		}
	// 重写run方法,当启动线程时,默认会执行run方法
	@Override
	public void run() {
		while (bean>0) { 
			robBean();
		}	
	}
}

三、实现Runnable接口-锁机制

public class BeanThreadImpl implements Runnable {
	// 1.声明一个成员变量用被每一个线程对象共享static修饰,
	private static int bean = 50;

	// 声明一个saleone方法表示买票功能
	private void saleOne() {
		Thread ct = Thread.currentThread();// 获取当前线程对象
		String name2 = ct.getName();// 获取当前线程对象名
		/**
		 * 线程同步方式一:同步代码块
		 * 9.2.1.基本语法结构
			synchronized (同步监听对象) {
				可能引发线程安全问题的代码
			}
		 */
		synchronized (BeanThreadImpl.class) {
			if (bean > 0) {
				System.out.println(name2+":当前豆子:" + bean);
				bean--;
			}
		}	
	}
	/**
	 * 构造方法
	 * 无参构造方法和带参构造方法 
	 */
	/**
	 * 重写run方法,方法中用while循环调用saleone方法
	 */
	@Override
	public void run() {
			while (bean > 0) {
				saleOne();
		}
	}
}

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Mxin5

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

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

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

打赏作者

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

抵扣说明:

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

余额充值