1、什么是线程?什么是多线程?
线程:轻量级进程,是操作系统进行调度的最小单位。
多线程:同时运行多个线程称为多线程。
2、多线程的实现有几种方法?区别是?为何要引入其他两个,用一个不行吗?
1>三种。(1)继承thread类,(2)实现runnable接口,(3)实现callable
2>继承thread类:优点:编码简单;缺点:继承了thread类,无法继承继承其他类,不利于扩展;
实现runnable接口:优点:线程任务类只是实现了Runnable接口,可以继续继承和实现。缺点:如果线程有执行结果是不能直接返回的。
实现callable接口:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
缺点:编程复杂
3>为了解决thread类不具有扩展性的缺点,但是后两种编码比较复杂。
3、什么是线程安全?如何解决线程安全?
1>线程安全:指的是多线程环境中,存在数据共享,一个线程访问的共享数据被其他线程修改了,就会出现线程安全问题。 在整个访问的过程中,无一共享的数据被其他线程修改,该线程就是安全的。程序运行过程中,使用了成员变量,且对成员变量数据进行了修改,就会存在数据共享问题,也就是线程安全问题。
2>如何解决线程安全问题:线程同步(核心思想加锁,同步代码块:synchronize)、
使用同步机制,使得在同一时间只能有一个线程修改共享数据,比如,同步代码块;
消除共享数据,即多个线程数据不共享或共享数据不被修改。
4、什么是线程池?临时线程什么时候创建?什么时候会开始拒绝任务?
1>线程池:可以复用线程的技术,提高线程的重复利用,减少资源浪费
2>新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程
3>核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝。
5.取钱案例
需求:小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元,模拟2人同时去取钱10万。
分析:
①:需要提供一个账户类,创建一个账户对象代表2个人的共享账户。
②:需要定义一个线程类,线程类可以处理账户对象。
③:创建2个线程对象,传入同一个账户对象。
④:启动2个线程,去同-一个账户对象中取钱10万。
package com.lyzd.theadsafe;
//模拟取钱案例
public class ThreadDemo {
public static void main(String[] args) {
// 1、定义线程类,创建一个共享的账户对象
Account acc=new Account("1111",100000);
//2、创建两个线程对象,代表小明和小红同时进来
new DrawThread(acc,"小红").start();
new DrawThread(acc,"小明").start();
}
}
package com.lyzd.theadsafe;
public class Account {
private String cradid;
private double money;
public Account() {
}
public Account(String cradid, double money) {
this.cradid = cradid;
this.money = money;
}
/**
小明,小红取钱
*/
public void drawMoney(double money) {
// 1、谁来取钱,线程的名字就是人名
String name=Thread.currentThread().getName();
if (this.money>=money){
System.out.println(name+"取钱成功,取走"+money);
this.money-=money;
System.out.println(name+"取钱后余额:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}
public String getCradid() {
return cradid;
}
public void setCradid(String cradid) {
this.cradid = cradid;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
package com.lyzd.theadsafe;
/**
取钱线程类
*/
public class DrawThread extends Thread{
// 接收处理的账户对象
private Account acc;
public DrawThread (Account acc,String name){
super(name);
this.acc=acc;
}
@Override
public void run() {
//小明小红取钱
acc.drawMoney(100000);
}
}
如果按这个思路会出现线程安全问题,我们可以利用同步代码块,同步方法或lock为其上锁,从而实现线程安全
同步代码块:对Account类中的drawMoney方法上锁
synchronized (this) {
if (this.money>=money){
System.out.println(name+"取钱成功,取走"+money);
this.money-=money;
System.out.println(name+"取钱后余额:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
同步方法:对Account类中的drawMoney方法上锁
public synchronized void drawMoney(double money) {
String name=Thread.currentThread().getName();
if (this.money>=money){
System.out.println(name+"取钱成功,取走"+money);
this.money-=money;
System.out.println(name+"取钱后余额:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}
lock:对Account类中的drawMoney方法上锁
String name=Thread.currentThread().getName();
//加锁
lock.lock();
try {
if (this.money>=money){
System.out.println(name+"取钱成功,取走"+money);
this.money-=money;
System.out.println(name+"取钱后余额:"+this.money);
System.out.println(10/0);
}else {
System.out.println(name+"来取钱,余额不足");
}
} finally {
//解锁
lock.unlock();
}