黑马程序员-java多线程

黑马程序员-java多线程

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ---------------------- 

 概念:

       1.进程

              进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程

进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。
       
      2.线程
              一个进程通常包含多个线程,一个线程成为控制单元,也叫执行路径,程序的执行都是线程在操作。线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
线程是程,序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
 
 利弊分析:
      java中多线程可以让计算机同时帮我们执行不同的代码,但这样做的后果就是,在计算机中出现过多的线程,从而导致CPU切换周期加大,也就影响了从大体角度来说总体的运行效率。
 
java中线程的五种状态:
 
如何创建线程?
 
    1.继承Thread类,并覆盖run方法
public class ThreadDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		MyThread thread=new MyThread("thread1");
		MyThread thread2=new MyThread("thread2");
		thread.start();//开启一个线程,并自动调用run方法
		thread2.start();
	}

}
//实现线程的第一种方式,继承Thread类,并覆盖run方法。
class MyThread extends Thread{
	private String name;
	public MyThread(String name){
		this.name=name;
	}
	//run方法里面封装要开启的线程所要执行的任务代码。
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println(name+"..."+i);
		}
	}
}

 注意:开启线程是通过调用start方法来完成的,而并非调用run方法。
 
    2.实现Runnable接口
public class ThreadDemo2 {
    public static void main(String[] args){
    	MyThread run=new MyThread();
    	//创建线程时给定Runnable接口。
    	Thread t=new Thread(run);
    	t.start();
    }
}
//创建线程第二种方式,通过实现Runnable接口。
// 这种方式是解决java中不能多继承的不足
class MyThread implements Runnable{

	@Override
	public void run() {
		
		System.out.println("开启线程要执行的代码");
		
	}
	
}
这种方式是为了解决当要开启多线程的类如果继承了别的类,那么就只能采用这种方式。同时这种方式还可以将共享数据独立出来。
 
  多线程存在安全问题:
 
//多线程安全问题产生的条件:
//1.存在多线程操作共享数据。
//2.操作共享数据的线程代码有多条。(当操作共享数据代只有一条语句时不会有安全问题)
//导致线程安全问题发生的原因:当一个线程在执行操作共享数据的多行代码过程中,其他线程参与了进来,就会导致线程安全问题的发生。
public class ThreadDemo2 {
    public static void main(String[] args){
    	MyThread2 run=new MyThread2();
    	Thread t=new Thread(run);
    	Thread t1=new Thread(run);
    	Thread t2=new Thread(run);
    	t.start();
    	t1.start();
    	t2.start();
    }
}
class MyThread2 implements Runnable{
    private int num=100;//多线程操作的共享数据
	@Override
	public void run() {
		//多线程操作共享数据代码
		while (true) {
			if (num > 0) {
				try {
					Thread.sleep(100);// 模拟当一个线程在执行操作共享数据代码时,其他线程进入。
				} catch (Exception e) {
				}
				System.out.println(Thread.currentThread().getName()
						+ "...sale..." + num--);
			}
                    else
                      break;			
               }
	}
	
}

运行结果:
....................Thread-1...sale...2
Thread-2...sale...1
Thread-0...sale...0
Thread-1...sale...-1
 
 如何解决多线程安全问题?
//解决多线程安全问题:加同步代码块
//同步代码块格式:
//synchronized(对象){}
//加同步的好处:解决了线程安全问题
//加同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁。
//同步的前提:同步中必须有多个线程并使用同一个锁。
public class ThreadDemo2 {
    public static void main(String[] args){
    	MyThread2 run=new MyThread2();
    	Thread t=new Thread(run);
    	Thread t1=new Thread(run);
    	Thread t2=new Thread(run);
    	t.start();
    	t1.start();
    	t2.start();
    }
}
class MyThread2 implements Runnable{
    private int num=100;//多线程操作的共享数据
    private Object object=new Object();//锁对象,锁旗标。
	@Override
	public void run() {
		//多线程操作共享数据代码
		while (true) {
			synchronized (object) {
				if (num > 0) {
					try {
						Thread.sleep(100);// 模拟当一个线程在执行操作共享数据代码时,其他线程进入。
					} catch (Exception e) {
					}
					System.out.println(Thread.currentThread().getName()
							+ "...sale..." + num--);
				} else break;
			}
		}
	}
	
}
public class SynchronizedDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		MyThread3 run=new MyThread3();
		Thread t=new Thread(run);
		Thread t2=new Thread(run);
		t.start();
		t2.start();
	}
}
class Bank{
	private int sum;
	//解决线程安全问题方式二:同步方法
	//同步方法,和同步代码块的效果一样,只是同步方法用的同步锁都是this,而同步代码块的锁是任何对象。
	public synchronized void add(int num){
		sum+=num;
		try {
			Thread.sleep(10);
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println("sum="+sum);
	}
}
class MyThread3 implements Runnable{
    Bank bank=new Bank();
	@Override
	public void run() {
		for(int i=0;i<3;i++){
			bank.add(100);
		}
	}
}
注意:静态同步方法的同步锁是该方法所属的字节码文件对象。
 多线程中死锁现象:
//死锁:发生场景一般在同步块嵌套。但这种情况也有和谐的时候,也就是说在同步嵌套时并不一定会死锁。
public class DeadLockDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Thread t1=new Thread(new Test(true));
		Thread t2=new Thread(new Test(false));
		t1.start();
		t2.start();

	}

}
class Test implements Runnable{
	private boolean flag;
	public Test(boolean flag){
		this.flag=flag;
	}
	public void run(){
		if(flag){
			while(true)
			synchronized (MyLock.locka) {
				 System.out.println("if....locka");
				 //线程到这里会去拿lockb锁,当如果有其他线程已经占有lockb锁,程序就会在这发生死锁。
				 //但如果该线程到这里刚好其他线程释放了lockb锁,而他就会得到lockb锁,释放locka锁,程序将继续执行。这就是有可能不会死锁的原因。
				synchronized (MyLock.lockb) {
					System.out.println("if....lockb");
				}
			}
		}else{
			while(true)
			synchronized (MyLock.lockb) {
				 System.out.println("else....lockb");
				synchronized (MyLock.locka) {
					System.out.println("else....locka");
				}
			}
		}
	}
}
//锁
class MyLock {
	public static final Object locka=new Object();
	public static final Object lockb=new Object();
	
}

我们在程序中应该尽量避免死锁的发生。
 
   线程的等待唤醒机制:
 
//线程的等待唤醒机制  wait() notify() notifyAll()
//为什么wait() notify() notifyAll()这些方法必须用在同步中?
//1.因为这些方法都是操作线程状态的方法
//2.因为在调用这些方法前必须明确要操作的是哪个监视器(锁)上的线程。
public class NotifyDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Resource r=new Resource();
		Output out=new Output(r);
		Input in=new Input(r);
		Thread t1=new Thread(out);
		Thread t2=new Thread(in);
		t1.start();
		t2.start();
	}

}
class Resource{
	 String name;
	 String sex;
	 boolean flag;
}
class Input implements Runnable{
	Resource resource;
	public Input(Resource resource){
		this.resource=resource;
	}
	public void run(){
		int x=0;
		while(true){
			synchronized (resource) {
				if(resource.flag)
					try {
						//同步锁resource.wait()方法,将持有同步锁resource的线程加到同步锁resource的线程池。
						resource.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				if (x == 0) {
					resource.name = "jiava";
					resource.sex = "nan";
				} else {
					resource.name = "夫人";
					resource.sex = "女女女女";
				}
				x = ++x % 2;
				resource.flag=true;
				//从同步锁resource的线程池中唤醒持有resource同步锁的某个线程。
				resource.notify();
			}
		}
	}
}
class Output implements Runnable{
	Resource resource;
	public Output(Resource resource){
		this.resource=resource;
	}
	public void run(){
		while(true){
			synchronized (resource) {
				if(!resource.flag)
					try {
						resource.wait();
						
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				System.out.println(resource.name+"....."+resource.sex);
				resource.flag=false;
				
				resource.notify();
			}
		}
	}
}
注意:同步中只有一个线程在执行,但活着的线程(具备执行资格不具备执行权)不只一个。
interrupt()方法清除线程的中断状态,强制性的,会抛InterruptedException异常。

 

wait()和sleep()区别:

 1.wait()可以不指定时间,而sleep()必须指定。

 2.在同步中,对cpu的执行权和锁的处理不同。

    wait()释放执行权和执行资格,释放锁。

    sleep()释放执行权和执行资格,不释放锁。


 
---------------------- ASP.Net+Unity开发 .Net培训 、期待与您交流! -----------------------
 
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值