Java学习笔记-多线程

-----------android培训java培训、java学习型技术博客、期待与您交流!------------ 

 

进程:正在执行中的一个程序。

线程:是一个正在执行的程序,进程中一个独立的控制单元。线程控制着进程的执行。

多线程:多线程的出现能让程序产生同时运行效果。可以提高程序执行效率。

线程状态:创建状态--->运行状态--->临时状态(具备执行资格,但没有执行权)----->冻结状态(放弃执行资格,进入临时状态)------>消亡状态。

创建线程方法:

1.继承Thread类,覆写run方法。

代码示例:

package ThreadDemo;

public class ThreadTest {
	public static void main(String [] args){
		Demo d = new Demo();//创建Demo对象,因为Demo继承了Thread所以创建Demo对象也是创建了一个线程。
		d.start();//start方法启动线程,并调用run方法
		for(int i=0;i<60;i++){
			System.out.println("main....run"+i);
		}
	}
}
class Demo extends Thread{//继承Thread类;
	@Override
	public void run(){//覆写run方法,run方法里存放线程执行的代码。
		for(int x=0;x<60;x++){
			System.out.println("demo....run"+x);
		}
	}
}


注意:启动线程必须用start方法,如果直接调用run方法,该线程并没有启动,相当于单线程,程序会按顺序执行。

2.实现Runnable,覆写run方法。

(1)已经有了Thread类为什么还要有Runnable接口:继承只能是单继承,如果一个类已经继承了其他类但是还有代码需要多线程执行,这时就没办法再继承Thread类(单继承Java不允许多继承,但可以通过实现接口,扩展功能)

(2)实现Runnable接口和继承Thread类的区别:

实现Runnable的好处:避免了继承的局限性。资源可以共享

继承Thread类线程代码存放在Thread子类的run方法中。

实现Runnable接口线程代码存放在接口子类的run方法中。

代码示例:

package ThreadDemo;

public class ThreadTest1 {
	public static void main(String [] args){
		TicketDemo td= new TicketDemo();
		new Thread(td).start();//创建线程,接受一个Runnable接口的子类,并启动线程
		new Thread(td).start();
		new Thread(td).start();
		new Thread(td).start();
	}
}
class TicketDemo implements Runnable{//实现Runnable接口
	private int ticket=100;
	@Override
	public void run(){//覆写run方法。
		while(true){
			if(ticket>0)
				System.out.println(Thread.currentThread().getName()+"...sale"+ticket--);
		}
	}
}

3.多线程安全:

当多线程操作共享数据时(多条语句),一个线程执行了一部分的语句,还没执行完,另一个线程参与执行,这样就会导致数据错乱,会出现安全问题。

同步可以解决多线程的安全问题:加同步后一个线程执行的时候会先拿到锁,当只执行完后以后会释放锁,下一个线程拿到锁才可以执行同步内的代码。

同步的前提:

(1)必须要有两个或两个以上的线程,

(2)必须多个线程使用同一个锁。

同步好处:解决线程的安全问题。

同步弊端:多线程需要判断锁,较为消耗资源。

同步代码块:自己需要指定一个锁。

代码示例:

Object obj = new Object();
private int sum=0;
public void add(int n){
	synchronized(obj){
		sum=sum+n;
		System.out.println(sum);
	}
}


同步函数:使用本类对象的锁(this)

private int sum=0;
public synchronized void  add(int n){
	sum=sum+n;
	System.out.println(sum);
}


静态同步函数:使用的是所在类的字节码文件对象(类名.class)

class bank{
	 private static int sum=0;
	 public static synchronized void  add(int n){
	  sum=sum+n;
	  System.out.println(sum);
	 }
}


4.线程间的通信:安全问题

注意:两个线程同时操作一个资源,对资源来说就是多线程,所以两个函数里的代码都需要同步,而且必须得用同一个锁。

package Thread;
public class InputOutpuDemo {
	public static void main(String [] args){
		Res r = new Res();
		Input in = new Input(r);
		Output out = new Output(r);
		new Thread(in).start();//创建线程传入存入的对象,启动线程。
		new Thread(out).start();
	}
}
class Res{//共享资源
	String name;
	String sex;
	boolean flag;
}
class Input implements Runnable{//实现runnable接口
	private Res r;
	Input(Res r){//定义构造函数接收共享资源
		this.r=r;
	}
	@Override
	public void run(){//覆写run方法
		int x=0;
		while(true){
			synchronized(r){//构造代码块使用的是共同资源对象
				if(x==0){
					r.name="mike";
					r.sex="man";
				}
				else{
					r.name="丽丽";
					r.sex="女女女女";
				}
				x=(x+1)%2;//切换存入数据
			}
		}
	}
}
class Output implements Runnable{//实现runnable接口
	private Res r;
	Output(Res r){//接收共同资源
		this.r=r;
	}
	@Override
	public void run(){//覆写run方法
		while(true){
			synchronized(r){//同步代码块,跟存入的代码都需要同步,使用同一个锁
				System.out.println(r.name+"..."+r.sex);
			}
		}
	}
}

5.等待唤醒机制:wait,notify;

注意:这里只有两个线程,每次唤醒都是对方。代码可以优化,在资源里设置赋值方法和取出方法,同步赋值和取出方法,同时实现等待唤醒机制,再线程代码里直接调用这两个方法就可以

package Thread;

public class InputOutpuDemo {
	public static void main(String [] args){
		Res r = new Res();//创建资源对象
		Input in = new Input(r);
		Output out = new Output(r);
		new Thread(in).start();//创建线程,并启动线程
		new Thread(out).start();
	}
}
class Res{
	String name;
	String sex;
	boolean flag;//定义标记达到存一次取一次的目的,不会乱
}
class Input implements Runnable{//实现runnable接口
	private Res r;
	Input(Res r){//构造函数接收共享资源
		this.r=r;
	}
	@Override
	public void run(){//覆写run方法
		int x=0;
		while(true){
			synchronized(r){//同步代码块,解决程序的安全问题
				if(r.flag)//flag默认为flase,条件为假,跳过wait向下执行
					try {
						wait();//会抛异常需要处理
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				if(x==0){//定义条件循环添加元素
					r.name="mike";//给共享资源属性赋值,也就是存入数据
					r.sex="man";
				}
				else{
					r.name="丽丽";
					r.sex="女女女女";
				}
				x=(x+1)%2;
				r.flag=true;//执行一次将条件改为true,让线程等待,等待前唤醒线程池里的线程
				notify();
			}
		}
	}
}
class Output implements Runnable{//实现runnable接口
	private Res r;
	Output(Res r){//构造函数接收共享资源
		this.r=r;
	}
	@Override
	public void run(){//覆写run方法
		while(true){
			synchronized(r){//同步代码块
				if(!r.flag)//条件为假(flase)等待,上一个线程已经讲条件改为true,跳过等待向下执行
					try {
						wait();
					} catch (InterruptedException e) {//处理异常
						e.printStackTrace();
					}
				System.out.println(r.name+"..."+r.sex);
				r.flag=false;//执行完更改条件让线程等待,等待前唤醒线程池里的线程。
				notify();
			}
		}
	}
}

6.JDK1.5以后出现了Condition接口。替代了Object的wait(),notify,notifyAll();

await()
signal()
signalAll()
Lock替代了synchronized;更好的解决了唤醒对方的问题。

解决了线程的唤醒不能唤醒对方的问题。

代码示例:

package ThreadDemo;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadLock {
	public static void main(String [] args){
		Stroage st =new Stroage();
		new Thread(new Input1(st)).start();//创建线程,
		new Thread(new Input1(st)).start();
		new Thread(new Input1(st)).start();
		new Thread(new Output1(st)).start();
		new Thread(new Output1(st)).start();
		new Thread(new Output1(st)).start();
	}
}
class Stroage{
	private int [] cells=new int [10];//定义数组,存储元素
	private int inpos,outpos;//定义角标
	private boolean flag;
	private Lock lock = new ReentrantLock();//创建锁对象,lock是接口只能用子类创建对象
	private Condition condition_in=lock.newCondition();//创建两个condition对象,condition是接口可以用lock.newCondition创建,一个用于存入一个用于输出,这样可以只唤醒对方线程。
	private Condition condition_out=lock.newCondition();
	public  void put(int num){//定义存数据的方法
		lock.lock();//获取所
		while(flag)
			try {
				condition_in.await();//condition等待方法替代了Object的wait方法,本方等待。
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		cells[inpos]=num++;
		System.out.println("在clles["+inpos+"]中存入数据--"+cells[inpos]);
		inpos++;
		if(inpos==cells.length)
			inpos=0;
		flag=true;
		condition_out.signal();//唤醒对方线程;
		lock.unlock();//释放锁。
		} 
	
	public  void out(){//定义取出数据的方法。
		lock.lock();//获取锁
		while(!flag)
			try {
				condition_out.await();//本方等待,
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("从cells["+outpos+"]中取出数据"+cells[outpos]);
			outpos++;
			if(outpos==cells.length)
				outpos=0;
			flag=false;
			condition_in.signal();//唤醒对方线程
			lock.unlock();//释放锁
		}
	
}
class Input1 implements Runnable{//实现Runnable接口
	private Stroage st;
	private int num;
	Input1(Stroage st){
		this.st=st;
	}
	@Override
	public void run(){//覆写run方法
		while(true){
			st.put(num++);
		}
	}
}
class Output1 implements Runnable{//实现Runnable接口
	private Stroage st;
	Output1(Stroage st){
		this.st=st;
	}
	@Override
	public void run(){//覆写run方法
		while(true){
			st.out();
		}
	}
}

7.中断线程,interrupt()

线程处于中断状态而又无法被唤醒时,interrupt可以强制清除中断状态。但会抛异常。

8.守护线程:setDeamon()

线程启动前标记为守护线程,当前台没有线程是,守护线程会自动结束。

9.join

特点:当A线程执行到了B线程的join方法时,A线程就会等待,等B线程运行结束后,A线程才会运行。

join可以临时加入线程执行。

10.线程优先级

setPriority()设置线程优先级,默认都是5,只有1-10.  MAX_PRIORITY(10)    MIN_PRIORITY(1)   NORM_PRIORITY(5).

11.yield  暂停当前正在执行的线程对象,并执行其他线程。

-----------android培训java培训、java学习型技术博客、期待与您交流!------------

 

 

你可能感兴趣的:(Java学习笔记-多线程)