继承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();
}
}
}