多线程
1) 多线程的好处:
① 资源利用率更高
② 简化编程模型
③ 简化异步事件的处理等
2) 多线程的弊端:
会造成死锁或其他问题。
3) 线程的状态:
a. 创建(NEW):Thread t = new Thread();
在堆中分配内存,尚未分配系统资源。
b. 就绪(RUNNABLE):t.start()
等待获得处理器,随时进入运行状态
c. 运行(RUNNING):
占用处理器,执行程序代码
d. 阻塞(BLOCKED):
线程因为某种原因放弃处理器,暂停运行,直到重新进入就绪状态
a) 等待池中阻塞——运行状态,执行wait()方法,JVM把线程放入等待池中
b) 对象锁池中阻塞——同步锁被其他线程占用时,JVM把线程放入对象锁池中
c) 其他阻塞——例如sleep(100)
e. 死亡(DEAD):线程执行完run方法或遇到异常而退出
4) JAVA的Thread线程类主要方法:
a. static Thread getCurrentThread():返回当前线程对象
b. String getName():返回线程名
c. void run():方法体中是创建出的线程需要执行的内容,需要被重写
d. static void sleep():休眠一段时间(毫秒),使当前线程由运行状态进入阻塞状态
e. void start():启动线程,进入就绪状态,线程运行时JVM会自动调用run()方法
f. void interrupt():http://blog.csdn.net/axman/article/details/562249
g. void setDaemon(boolean on):设置为守护线程,必须在start之前进行,不需要自动结束,只剩下守护线程时自动结束。后台线程。
h. void join():主线程等待调用join方法的子线程执行结束后再继续执行
i. void setPriority(int p):设置优先级(1-10)Thread.MAX_PRIORITY,Thread.MIN_PRIORITY,Thread.NORM_PRIORITY
j. static void yield():暂停当前正在执行的线程对象,并执行其他线程.由执行状态变成就绪状态。
5) Object类关于线程的方法:
a. void wait():使当前线程由运行状态进入进入阻塞状态,直到被notify()方法唤醒
b. void notify():唤醒这个对象的线程。若有多个,随机唤醒任意一个
c. void notifyAll():唤醒这个对象的所有线程
6) JAVA创建一个线程的方法:
a) 继承Thread类,重写run()方法。
b) 实现runnable接口,实现run()方法
例如:
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
//开启线程,自动执行run()方法
demo1.start();
}
}
//继承Thread类
class Demo1 extends Thread{
//重写run()
@Override
public void run(){
//code...
}
}
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
Thread t = new Thread(demo2);
//开启线程,自动执行run()方法
t.start();
}
}
class Demo2 implements Runnable{
//实现run()
public void run() {
//code...
}
}
7) 多线程安全问题产生的原因
多个线程操作共享的数据,并且处理共享数据的语句有多条。
例如:
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
//线程t1,t2进入就绪状态,随时开始运行线程。
t1.start();
t2.start();
}
}
//实现Runnable接口,重写run方法。
class Demo implements Runnable {
private int num=100;
@Override
//t1线程,num=100,输出Thread-0----100,切换到t1。
//t2线程,num=100,输出Thread-1----100,切换到t2。
//这样,num--还未执行,便切换了线程,则有两个线程同时执行了100,产生了错误。
public void run() {
while(num>0)
{
System.out.println(Thread.currentThread().getName()+"----"+num);
num--;
}
}
}
8) 解决多线程安全问题
同步:在多线程编程里,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。同步降低了一点效率(切换到其他进程时要判断同步锁),但是是可以接受的。锁可以是任意对象,但必须是同一对象。
例如:
public class CreateThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}
class Demo implements Runnable {
private int num=100;
//应该把obj作为一个成员变量定义在这里,只创建了一个Demo对象,所以也只有一个obj对象
//Object obj = new Object();
@Override
public void run() {
Object obj = new Object();
//每次调用run方法都创建一个obj,不是同一个对象,无法实现同步。
synchronized(obj){
while(num>0)
{
System.out.println(Thread.currentThread().getName()+"----"+num);
num--;
}
}
}
}
9) JAVA的同步原理:将多条处理共享数据的语句封装在一个代码块中,一个线程获得同步锁,执行完代码块中所有语句后,释放同步锁,其他线程才能进入。
a) 同步代码块:将代码块生命为synchronized,并加同步锁。
b) 同步函数:将函数声明为synchronized,自动以当前对象作为同步锁。
例如:
同步代码块
public class CreateThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}
class Demo implements Runnable {
private int num=100;
//Object obj = new Object();
@Override
public void run() {
synchronized(obj){
while(num>0)
{
System.out.println(Thread.currentThread().getName()+"----"+num);
num--;
}
}
}
}
同步函数
public class CreateThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t2.start();
t1.start();
}
}
class Demo implements Runnable {
private int num=100;
Object obj = new Object();
@Override
//定义同步函数,可以定义run方法,也可以定义其他方法,自动调用run方法。
public synchronized void run() {
while(num>0)
{
System.out.println(Thread.currentThread().getName()+"----"+num);
num--;
}
}
}
10)死锁:两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
例如:同步的嵌套
public class DeadLock {
public static void main(String[] args) {
Demo demo = new Demo();
Thread t1 = new Thread(demo);
Thread t2 = new Thread(demo);
t1.start();
t2.start();
}
}
class Demo implements Runnable{
private static final Object o1 = new Object();
private static final Object o2 = new Object();
public void run(){
//一直循环直到发生死锁。
while(true){
//同步锁o1需要o2时,输出o1 need o2
//同步锁o1得到o2时,输出o1 get o2
synchronized(o1){
System.out.println(Thread.currentThread().getName()+" o1 need o2");
synchronized(o2){
System.out.println(Thread.currentThread().getName()+" o1 get o2");
}
}
//同步锁o2需要o1时,输出o2 need o1
//同步锁o2得到o1时,输出o2 get o1
synchronized(o2){
System.out.println(Thread.currentThread().getName()+" o2 need o1");
synchronized(o1){
System.out.println(Thread.currentThread().getName()+" o2 get o1");
}
}
}
}
}
输出结果o1想要o2,o2想获得o1对象锁,相互不妥协,发生死锁。
Thread-0 o1 need o2
Thread-0 o1 get o2
Thread-0 o2 need o1
Thread-1 o1 need o2
11)线程间通信:wait(),notify(),notifyAll()
a.单生产者单消费者
例如,两个线程操作同一资源(Resource),一个线程不断赋值,一个线程不断取值(即生产者与消费者问题)。要求赋一个值,就取出一个值。两个线程通过判断Resource中是否有值,发出wait和notify。代码如下:
public class Thread_tongxin {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
class Resource{
private String name;
private String sex;
//flag用于判断资源是否有值
private boolean flag = false;
public synchronized void set(String name,String sex){
//如果资源已被赋值,则wait
if(flag==true){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag=true;
//notify方法任意唤醒线程池中的一个线程,这里唤醒的是t1或者t2,如果为t1,判断flag=true,
//则执行wait()方法,若为t2,执行Output中的run方法,将资源取出。
this.notify();
}
public synchronized void out(){
if(flag==false){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread()+name+","+sex);
flag=false;
this.notify();
}
}
//Input类 负责给资源赋值
class Input implements Runnable{
private Resource r;
//构造函数,初始化时就将资源r传进来
Input(Resource r){
this.r = r;
}
public void run() {
int i = 0;
while(true){
//synchronized只需要包括操作共享资源的语句!不能包括while(true)
//两个set方法交替赋值,模拟不断赋值的情况
if(i++%2==0){
r.set("奇犽-揍敌客","boy");
}else{
r.set("尼飞彼多","girl");
}
}
}
}
//Output类 负责将资源取出
class Output implements Runnable{
private Resource r;
Output(Resource r){
this.r = r;
}
public void run() {
while(true){
r.out();
}
}
}
b.多生产者多消费者
多个生产者和消费者,需要修改两个地方,才能使线程运行正常。
对资源的进行判断时将if改为while——使用if会造成线程当程序中只有线程t0和t1存活时,某一线程不判断flag的值,造成生产的对象不被消费的情况。while可以避免该情况。
将notify方法改为notifyAll方法,四个线程t0,t1,t2,t3,t0和t1负责生产,t2和t3负责消费,存在值存活t0和t1时,所有线程都处于wait的状态。notifyAll可以避免该状态,但是降低了效率,因为生产时,只需要唤醒消费的线程即可,却唤醒了所有线程。
例如:
public class Thread_tongxin2 {
public static void main(String[] args) {
Res r = new Res();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
//资源类,两个同步方法,produce生产,consume消费,flag判断资源是否为空,count记录生产个数
class Res{
private String name;
private boolean flag;
private int count=1;
public synchronized void produce(){
//必须while,若为if则存在一种情况:只有t0存活时,t0判断后wait,唤醒t1,t1不做判断继续往下执行。下同
while(flag==true){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = "烤鸭"+count++;
flag=true;
System.out.println(Thread.currentThread().getName()+" 生产了 "+this.name);
//若为notify方法,则存在发生死锁的情况,此时四个线程都处于wait阻塞状态。下同
//例如,只有t0存活,t0wait后唤醒t1,t1也判断flag后也wait,则没有存活的线程了。
this.notifyAll();
}
public synchronized void consume(){
while(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag=false;
System.out.println(Thread.currentThread().getName()+" consume "+this.name);
this.notifyAll();
}
}
//生产者
class Producer implements Runnable{
private Res r;
Producer(Res r){
this.r = r;
}
@Override
public void run() {
while(true){
r.produce();
}
}
}
//消费者
class Consumer implements Runnable{
private Res r;
Consumer(Res r){
this.r = r;
}
@Override
public void run() {
while(true){
r.consume();
}
}
}
c.Synchronized的改良——Lock接口和Condition接口
前面例子中生产者生产后本应唤醒消费者的进程,notifyAll方法却将生产者消费者的线程都开启,造成了效率的降低。JDK1.5将同步(Sychronized)和锁(Object)封装成了对象——Lock和Condition,将原来隐式动作变成了显式动作,且一个锁可以获取多个监视器,更加灵活,可以解决效率降低的问题。
Lock接口的方法:
void lock():获取锁,同步的代码开始
void unlock():释放锁,若发生异常不会执行,所以常用于finally代码块中,同步的代码结束
newCondition():获取一个同步锁对象(监视器),可以获取多个
Condition接口的方法:
await():相当于wait
signal():相当于notify
signalAll():相当于notifyAll
例如:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Thread_tongxin3 {
public static void main(String[] args) {
Res1 r = new Res1();
Producer1 p = new Producer1(r);
Consumer1 c = new Consumer1(r);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
class Res1{
Lock lock = new ReentrantLock();//互斥锁,实现了Lock接口
//创建两个监视器,一个监视生产者,一个监视消费者
Condition con1 = lock.newCondition();
Condition con2 = lock.newCondition();
private String name;
private boolean flag;
private int count=1;
public void produce(){
//去掉synchronize,lock方法开始同步代码块
lock.lock();
try {
while(flag==true){
try {
con1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = "烤鸭"+count++;
flag=true;
System.out.println(Thread.currentThread().getName()+" 生产了 "+this.name);
con2.signalAll();
}finally{
//使用try-finally代码块释放锁
lock.unlock();
}
}
public void consume(){
try {
lock.lock();
while(!flag){
try {
con2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
flag=false;
System.out.println(Thread.currentThread().getName()+" consume "+this.name);
con1.signalAll();
}finally{
lock.unlock();
}
}
}
//生产者
class Producer1 implements Runnable{
private Res1 r;
Producer1(Res1 r){
this.r = r;
}
@Override
public void run() {
while(true){
r.produce();
}
}
}
//消费者
class Consumer1 implements Runnable{
private Res1 r;
Consumer1(Res1 r){
this.r = r;
}
@Override
public void run() {
while(true){
r.consume();
}
}
}