JAVA多线程基础

多线程

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






 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值