程序:可以理解为一组静态的代码 进程:正在进行的程序 线程:正则执行线程的小单元
多线程 线程允许我们并发执行多个代码片段。
创建线程有两种方式, 方式一: 定义一个类继承Thread并重写run方法。run方法用来定义需要线程并发执行的任务代码 第二种创建线程的方式:实现Runnable接口单独定义线程任务 第一种创建线程的方式 优点:创建简单,当临时需要执行某个任务时使用这种方式创建更直接 缺点:由于java是单继承的,这会导致我们继承了线程以后就不能再继承其他类去复用方法,实际开发非常不便
定义线程的同时重写run方法,将任务也一同定义出来这会导致线程与任务有一个必然耦合关系,不利于线程的重用。
注意,启动线程要调用start,而不是直接调用run方法。 start方法调用完毕后,线程纳入线程调度器。当该线程第一次获得CPU时间开始运行时,其run方法 会自动被调用。
线程纳入线程调度器后,只能被动的等待分配CPU的时间片,得到时间片后,CPU便会运行该线程的任务代码,时间片用完后,CPU离开,此时线程调度会再分配时间片给某个线程使其运行。
线程调度器分配时间片的概率是一样的,但是所有并发运行的线程不保证"一人一次"这样均匀的分配 时间片。
匿名内部类实现线程
Thread t1
= new Thread() {
public void run() {
for (int i
= 0; i
< 20; i
++) {
System
.out
.println("Thread"+i
);
}
};
};
Runnable r1
= new Runnable() {
@Override
public void run() {
for (int i
= 0; i
< 20; i
++) {
System
.out
.println("Runnable"+i
);
}
}
};
t1
.start();
new Thread(r1
).start();
生产消费者模型
仓库类
public class Cangku {
private ArrayList
<Integer> list
= new ArrayList<Integer>();
public void add() {
if (list
.size() < 20) {
list
.add(1);
System
.out
.println("++++");
}else {
return;
}
}
public void remove() {
if (list
.size() > 0) {
list
.remove(0);
System
.out
.println("------");
} else {
return;
}
}
}
生产者
public class Sheng extends Thread{
public Sheng(Cangku cangku
) {
super();
this.cangku
= cangku
;
}
private Cangku cangku
;
@Override
public void run() {
while(true) {
cangku
.add();
}
}
}
消费者
public class Xiao extends Thread{
public Xiao(Cangku cangku
) {
super();
this.cangku
= cangku
;
}
private Cangku cangku
;
@Override
public void run() {
while(true) {
cangku
.remove();
}
}
}
main
public static void main(String
[] args
) {
Cangku cangku
= new Cangku();
Sheng sheng
= new Sheng(cangku
);
sheng
.start();
Xiao xiao
= new Xiao(cangku
);
xiao
.start();
Xiao xiao1
= new Xiao(cangku
);
xiao1
.start();
}
产生问题
两个消费者同时访问一个仓库对象,仓库内只有一个元素时,两个消费者并发访问,有可能产生抢夺资源的问题
解决 让仓库对象被线程访问时,仓库对象被锁定 让仓库对象只能被一个对象访问,其他线程处于等待状态,即同一个时间点只有一个线程访问 特征修饰符:synchronized 两种写法
写在方法的结构上(锁定的时调用方法时的那个对象)
public synchronized void remove() {
写在方法/构造方法/块的内部
public void test(){
synchronized (this){
}
}
最终解决,添加线程安全锁,让线程等待或唤醒
public class Cangku {
private ArrayList
<Integer> list
= new ArrayList<Integer>();
public void add() {
if (list
.size() < 20) {
list
.add(1);
System
.out
.println("++++");
} else {
try {
this.notifyAll();
this.wait();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
}
public void remove() {
if (list
.size() > 0) {
list
.remove(0);
System
.out
.println("------");
} else {
try {
this.notifyAll();
this.wait();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
}
}
注意
当没有线程安全锁时,调用 wait();方法,可能抛出异常
IllegalMonitorStateException
是因为当通知一个人等待是,线程切换了,被通知的那个人已经不是刚才的那个线程了 此时,如果有线程安全锁,就不会产生问题了
总结
利用了线程安全锁,锁定的是对象 利用方法控制线程状态来回切换
笔试题
slepp方法是Thread类下的,方法是静态的,类名.调用,在哪个位置调用,哪个位置等待,自己就可以醒,不会释放锁 wait方法是Object下的,通过对象.调用,是让访问对象的其他线程等待。需要其他对象调用notify唤醒,等待后会释放锁
join方法
线程的API
线程提供了一个静态方法: static Thread currentThread() 这个方法会将运行这个方法的线程返回。 一个重要的API:ThreadLocal会使用到它。
static Thread currentThread() 返回对当前正在执行的线程对象的引用。
Thread t1
= Thread
.currentThread();
System
.out
.println(t1
);
new Thread() {
@Override
public void run() {
System
.out
.println(Thread
.currentThread());
}
}.start();
Thread main
= Thread
.currentThread();
long id
= main
.getId();
System
.out
.println("id:" + id
);
String name
= main
.getName();
System
.out
.println(name
);
int priority
= main
.getPriority();
System
.out
.println(priority
);
boolean isAlive
= main
.isAlive();
boolean isDaemon
= main
.isDaemon();
boolean isInterrupted
= main
.isInterrupted();
System
.out
.println("isAlive:" + isAlive
);
System
.out
.println("isDaemon:" + isDaemon
);
System
.out
.println("isInterrupted:" + isInterrupted
);
线程优先级
线程启动后纳入到线程调度,线程时刻处于被动获取CPU时间片而无法主动获取。我们可以通过调整线程的优先级来最大程度的干涉线程调度分配时间片的几率。 理论上优先级越高的线程获取CPU时间片的次数越多。
线程优先级有10个等级,分别用整数1-10表示。其中1位最低优先级,10为最高优先级,5为默认值。
调用线程的方法:setPriority()方法来设置优先级。
处理中断异常
:InterruptedException
public static void main(String
[] args
) {
Thread lin
= new Thread() {
public void run() {
System
.out
.println("林:刚美完容,睡一会...");
try {
Thread
.sleep(10000);
} catch (InterruptedException e
) {
System
.out
.println("林:干嘛呢!干嘛呢!干嘛呢!都破了相了!");
}
System
.out
.println("林:醒了!");
}
};
Thread huang
= new Thread() {
public void run() {
System
.out
.println("黄:开始砸墙!");
for(int i
=0;i
<5;i
++) {
System
.out
.println("黄:80!");
try {
Thread
.sleep(1000);
} catch (InterruptedException e
) {
}
}
System
.out
.println("咣当!");
System
.out
.println("黄:搞定!");
lin
.interrupt();
}
};
lin
.start();
huang
.start();
}
/**
* 守护线程
* 设置守护线程必须在线程启动之前设置
* 守护线程又称为后台线程,默认创建出来的线程都是普通线程。
* 守护线程需要通过调用线程方法:setDaemon(boolean on)来
* 进行设置。
*
* 守护线程使用上与普通线程没有区别,但是在结束时机上有一个
* 不同之处:进程结束时,所有正在运行的守护线程都会被强制停
* 止。而进程结束:当一个进程中所有的普通线程结束时进程结束
*
* GC就是跑在守护线程上的。
* 我们也可以将某些会一致运行的任务,并且当主要业务执行完毕
* 后可以跟着一同结束的就都放在守护线程上运行即可。
*
* @author Administrator
*
*/
守护线程
public class DaemonThreadDemo {
public static void main(String
[] args
) {
Thread rose
= new Thread() {
public void run() {
for(int i
=0;i
<5;i
++) {
System
.out
.println("rose:let me go!");
try {
Thread
.sleep(1000);
} catch (InterruptedException e
) {
}
}
System
.out
.println("rose:啊啊啊啊AAAAAaaaaa....");
System
.out
.println("噗通!");
}
};
Thread jack
= new Thread() {
public void run() {
while(true) {
System
.out
.println("jack:you jump!i jump!");
try {
Thread
.sleep(1000);
} catch (InterruptedException e
) {
}
}
}
};
rose
.start();
jack
.setDaemon(true);
jack
.start();
while(true) {
}
}
}
join:让线程完成排队
public class A extends Thread {
@Override
public void run() {
System
.out
.println("线程a开始执行");
B b
= new B();
b
.start();
try {
b
.join();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
System
.out
.println("线程a结束执行");
}
}
public class B extends Thread{
@Override
public void run() {
System
.out
.println("线程b开始执行");
new C(this).start();;
try {
Thread
.sleep(3000);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
System
.out
.println("线程b结束执行");
}
}
public class C extends Thread {
private B b
;
public C(B b
) {
this.b
= b
;
}
@Override
public void run() {
System
.out
.println("线程c开始执行");
synchronized (b
) {
try {
Thread
.sleep(5000);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
System
.out
.println("线程c结束执行");
}
}
public static void main(String
[] args
) {
new A().start();
}
public class ThreadTest extends Thread {
static boolean b
= false;
public static void main(String
[] args
) {
final Thread t1
= new Thread() {
public void run() {
for(int i
=1;i
<=10;i
++) {
System
.out
.println("图片下载了"+i
);
try {
Thread
.sleep(100);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
b
= true;
}
};
Thread t2
= new Thread() {
public void run() {
try {
t1
.join();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
if(b
) {
System
.out
.println("图片显示成功");
}else {
System
.out
.println("图片显示失败");
throw new RuntimeException("图片显示失败");
}
}
};
t1
.start();
t2
.start();
}
}
哲学家就餐问题
public class Kuaizi extends Thread {
private int num
;
public int getNum() {
return num
;
}
public void setNum(int num
) {
this.num
= num
;
}
public Kuaizi(int num
) {
super();
this.num
= num
;
}
}
public class Zhexuejia extends Thread{
public Zhexuejia(String name
, Kuaizi zuo
, Kuaizi you
) {
super();
this.name
= name
;
this.zuo
= zuo
;
this.you
= you
;
}
private String name
;
private Kuaizi zuo
;
private Kuaizi you
;
@Override
public void run() {
synchronized (zuo
) {
System
.out
.println(name
+"获得左手的筷子");
synchronized (you
) {
System
.out
.println(name
+"获得右手的筷子");
System
.out
.println(name
+"......");
}
}
}
}
public static void main(String
[] args
) {
Kuaizi kuaizi1
= new Kuaizi(1);
Kuaizi kuaizi2
= new Kuaizi(2);
Kuaizi kuaizi3
= new Kuaizi(3);
Kuaizi kuaizi4
= new Kuaizi(4);
Zhexuejia zhexuejia1
= new Zhexuejia("张1三",kuaizi1
,kuaizi2
);
Zhexuejia zhexuejia2
= new Zhexuejia("张2三",kuaizi2
,kuaizi3
);
Zhexuejia zhexuejia3
= new Zhexuejia("张3三",kuaizi3
,kuaizi4
);
Zhexuejia zhexuejia4
= new Zhexuejia("张4三",kuaizi4
,kuaizi1
);
zhexuejia1
.start();
zhexuejia2
.start();
zhexuejia3
.start();
zhexuejia4
.start();
}
可能会产生死锁的问题
解决,增加排队的机制
public Zhexuejia(String name
, Kuaizi zuo
, Kuaizi you
,long time
) {
super();
this.name
= name
;
this.zuo
= zuo
;
this.you
= you
;
this.time
= time
;
}
private String name
;
private Kuaizi zuo
;
private Kuaizi you
;
private long time
;
@Override
public void run() {
try {
Thread
.sleep(time
);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
synchronized (zuo
) {
System
.out
.println(name
+"获得左手的筷子");
synchronized (you
) {
System
.out
.println(name
+"获得右手的筷子");
System
.out
.println(name
+"......");
}
}
}
同时创建Zhexuejia对象需要在传递一个时间参数
并发安全问题的介绍
同步锁
/**
* 多线程并发安全问题
* 当多个线程并发操作同一个资源(临界资源)时,由于线程切换
* 实际不确定,导致执行此操作的顺序混乱,会出现执行过程
* 与结果未按照该程序预定方式进行导致一系列后果,严重时可
* 能导致系统瘫痪。
* @author Administrator
*
*/
产生了抢资源的问题
public class ThreadTest extends Thread {
public static void main(String
[] args
) {
final Table table
= new Table();
Thread t1
= new Thread() {
public void run() {
while(true) {
int bean
= table
.getBean();
Thread
.yield();
System
.out
.println(getName()+":"+bean
);
}
}
};
Thread t2
= new Thread() {
public void run() {
while(true) {
int bean
= table
.getBean();
Thread
.yield();
System
.out
.println(getName()+":"+bean
);
}
}
};
t1
.start();
t2
.start();
}
}
class Table {
int beans
= 20;
解决方式一
public synchronized int getBean() {
if(beans
==0) {
throw new RuntimeException("没有豆子了!");
}
Thread
.yield();
return beans
--;
}
}
解决方式二
/**
* 同步块:
* synchronized(同步监视器对象){
* 需要同步运行的代码片段
* }
*
* 同步块可以更准确的控制需要同步运行的代码片段,有效的
* 缩小同步范围可以保证并发安全的前提下尽可能的提高并发
* 的效率。
*
* 同步块要求指定一个同步监视器对象,即:上锁
* 的对象。这个对象可以是java中任何对象,但是
* 必须保证多个需要同步运行此代码片段的线程看到
* 的该对象是"同一个"。
*/
public void buy() {
Thread t
= Thread
.currentThread();
try {
System
.out
.println(t
.getName()+":正在挑衣服...");
Thread
.sleep(5000);
synchronized (this) {
System
.out
.println(t
.getName()+":正在试衣服...");
Thread
.sleep(5000);
}
System
.out
.println(t
.getName()+":结账离开");
} catch (Exception e
) {
}
}
/**
* 静态方法若使用synchronized修饰,那么这个方法一定
* 具有同步效果。
* 静态方法上使用的同步监视器对象为这个类的"类对象",
* 每个java定义的类都只有唯一的一个类对象(Class类型的的实
* 例),而类对象后面的反射知识中会介绍。
*
* 静态方法中若使用同步监视器对象,也可以使用
* 当前类的类对象,获取当前类的类对象可以直接
* 通过:类名.class得到。如下:
*/
互斥锁
/**
* 互斥锁
* 当使用synchronized锁定多个代码片段,并且他们指定的
* 同步监视器对象是同一个时,那么这些代码片段之间就是互斥
* 的,多个线程不能同时在这些代码片段中运行。
*/
public class SyncDemo4 {
public static void main(String
[] args
) {
Boo boo
= new Boo();
Thread t1
= new Thread() {
public void run() {
boo
.methodA();
}
};
Thread t2
= new Thread() {
public void run() {
boo
.methodB();
}
};
t1
.start();
t2
.start();
}
}
class Boo{
public synchronized void methodA() {
try {
Thread t
= Thread
.currentThread();
System
.out
.println(t
.getName()+":正在执行A方法...");
Thread
.sleep(5000);
System
.out
.println(t
.getName()+":执行A方法完毕!");
} catch (Exception e
) {
}
}
public void methodB() {
synchronized(this) {
try {
Thread t
= Thread
.currentThread();
System
.out
.println(t
.getName()+":正在执行B方法...");
Thread
.sleep(5000);
System
.out
.println(t
.getName()+":执行B方法完毕!");
} catch (Exception e
) {
}
}
}
}