黑马程序员_JAVA之多线程

原创 2015年07月06日 21:59:39
<h1 style="margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: Arial; line-height: 26px;"><span style="font-family: SimHei;"><span style="font-family: Simsun;">------- </span><a target=_blank target="_blank" href="http://www.itheima.com/" style="color: rgb(51, 102, 153); text-decoration: none; font-family: Simsun;">android培训</a><span style="font-family: Simsun;">、</span><a target=_blank target="_blank" href="http://www.itheima.com/" style="color: rgb(51, 102, 153); text-decoration: none; font-family: Simsun;">java培训</a><span style="font-family: Simsun;">、期待与您交流!----------</span></span></h1>
多线程
一:基本概念
线程与进程
线程:是依赖于进程的执行绪(执行路径/控制单元),是程序使用CPU的基本单位。
进程:当前正在执行的程序,代表一个应用程序在内存中的执行区域。
多进程:同一时间段内执行多个任务。同一时刻只能执行一个任务。如Windows为代表的操作系统。多进程并不提高某个程序的执行速度,仅仅是提高了CPU的使用率。真正的多进程执行是指多核同时计算。
单线程:一个进程中,只有一个线程执行。
多线程:同一个进程中,多个线程执行。这多个线程共享该进程资源(堆内存与方法区),栈内存独立,即每一个线程占用一个栈。
线程的两种调度方式
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。
抢占式调度模型 :优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个。(线程随机性)
注:java使用的是抢占式调度模型
线程并行与线程并发
线程并行:正常的多线程执行就是线程并行。即逻辑上同一时间同时运行。就是在执行时每个线程都在不断地根据自己的优先级在抢CPU
线程并发:由于线程抢占而不应出现的某一时刻的线程及相关数据状态。如并发修改异常的产生。
JVM的启动支持多线程
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

二:多线程的实现方式以及相关方法

一)多线程的实现方式

方式一:继承Thread类
自定义线程类继承Thread类。
重写run方法。run方法内为该线程执行代码。将其理解为其他线程的main方法,即该线程的执行入口。
使用:
创建线程对象
开启线程,即调用start方法,该方法会自动调用这个线程的run方法。
方式二:实现Runnable接口
定义自己的类(线程执行目标类),实现Runnable接口
创建线程执行目标类对象
使用线程执行目标对象创建线程对象
调用线程的start方法开启线程
方式一和方式二的代码实现用到的方法
构造方法:
public Thread(Runnable target) 指定线程执行目标

普通方法:

public final String getName()

public final void setName(String name)

public static Thread currentThread()

public void run()

public void start()

重写public String toString()

实现多线程的方式:
public void start()  开启线程
public final String getName()  获取线程名称
public final void setName(String name)  设置线程名称
public static Thread currentThread() 获取当前的线程

代码演示

package cn.itcast;

public class Demo2 {

	public static void main(String[] args) {

		method();
		System.out.println("=========");
		method2();
	}
	//多线程创建方式一
	public static void method(){
		
		MyThread mt = new MyThread("唐嫣");
		//mt.setName("唐嫣");
		MyThread mt2 = new MyThread("赵丽颖");
		//mt.setName("赵丽颖");
		MyThread mt3 = new MyThread("斯琴高丽");
		//mt.setName("斯琴高丽");
		/*
		 * 如果调用run方法,就是调用了一个普通对象的普通方法,没有新线程
		 * mt.run();
		 * mt2.run();
		 * mt3.run();
		 */
		
		//调用start方法开启线程
		mt.start();
		mt2.start();
		mt3.start();
	}
	//多线程创建方式二
	public static void method2(){
		//创建多线程执行目标类对象
		MyRunnable mr = new MyRunnable();
		
		//通过线程执行目标类对象,创建线程对象
		Thread t1 = new Thread(mr,"唐嫣");
		Thread t2 = new Thread(mr,"诗诗");
		Thread t3 = new Thread(mr,"颖颖");
		
		//开启线程
		t1.start();
		t2.start();
		t3.start();
	}
}

MyThread的代码

package cn.itcast;
/*
 * 定义自己的线程类
 */
public class MyThread extends Thread {

	//空参构造
	public MyThread() {
		super();
	}
	
	//有参构造
	public MyThread(String name) {
		super(name);
	}
	//重写该线程中的"main"方法,实际上就是run方法
	@Override
	public void run() {
		
		String name2 = getName();
		for (int i = 0; i < 100; i++) {
			System.out.println(name2+"--"+i);
		}
	}

}

MyRunnable代码

package cn.itcast;
/*
 * 定义线程执行目标类
 */
public class MyRunnable implements Runnable {

	//重写run方法,供线程调用
	@Override
	public void run() {
		
		//获取当前执行的线程
		Thread currentThread = Thread.currentThread();
		String name = currentThread.getName();

		for (int i = 0; i < 100; i++) {
			System.out.println(name+"=="+i);
		}
	}

}
从上面可以发现方式一和方式二的区别

方式一:如果一个类明确了自己的父类,那么它就不可以再继承Thread。因为java不允许类的多继承。

方式二:可以让业务类不再继承Thread而专注于业务继承其他类,避免了单继承的局限性。

二)相关方法

1)线程的优先级

概念:线程优先级代表了抢占CPU的能力。优先级越高,抢到CPU执行的可能性越大。

优先级相关方法:

public final void setPriority(int newPriority) 优先级取值:1-10

public final int getPriority()

代码实现

package cn.itcast2;
/*
 * 优先级的范围:
 * 		1-10  默认5
 */
public class Demo {

	public static void main(String[] args) {

		MyThread mt = new MyThread("唐嫣");
		MyThread mt2 = new MyThread("诗诗");
		
		mt.setPriority(10);
		mt2.setPriority(1);
		
		mt.start();
		mt2.start();
	}

}

2)线程休眠

概念:指定线程休眠一定时间,进入等待状态。在该段时间结束后,线程重新可执行。

代码实现

package cn.itcast3;
/*
 * 线程休眠
 * 相关方法
 * public static void sleep(long millis)  在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
                  throws InterruptedException   
 */
public class Demo {

	public static void main(String[] args) {

		MyThread mt = new MyThread("唐嫣");
		MyThread mt2 = new MyThread("诗诗");
		
		mt.setPriority(10);
		mt2.setPriority(1);
		
		mt.start();
		mt2.start();
	}

}

MyThread的代码

package cn.itcast3;

public class MyThread extends Thread {

	
	public MyThread() {
		super();
	}

	public MyThread(String name) {
		super(name);
	}

	@Override
	public void run() {
		
		
		for (int i = 0; i < 100; i++) {
			if(i==20) {
				try {
					//定义线程休眠时间为10000毫秒
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(getName()+":"+i);
		}
	}

}

3)加入线程

概念:等待某加入的线程终止后再执行。

代码实现

package cn.itcast4;
/*
 * 线程加入
 * public final void join()  等待该线程终止。
                throws InterruptedException
   即  调用方法时所在的线程,等待调用方法的线程执行完毕
 */
public class Demo {

	public static void main(String[] args) throws InterruptedException {

		MyThread mt = new MyThread("唐嫣");
		MyThread mt2 = new MyThread("诗诗");
		
		mt.setPriority(10);
		mt2.setPriority(1);
		
		mt.start();
		mt2.start();
		
		mt.join();
		
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}

}

4)线程礼让

概念:暂停当前正在执行的线程对象,并执行其他线程。

代码实现

package cn.itcast5;
/*
 * 线程礼让
 * public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。
 */
public class Demo {

	public static void main(String[] args) throws InterruptedException {

		MyThread mt = new MyThread("唐嫣");
		MyThread mt2 = new MyThread("诗诗");

		mt.start();
		mt2.start();
		
	}

}

package cn.itcast5;

public class MyThread extends Thread {

	
	public MyThread() {
		super();
	}

	public MyThread(String name) {
		super(name);
	}

	@Override
	public void run() {
		
		
		for (int i = 0; i < 100; i++) {
			System.out.println(getName()+":"+i);
			//定义线程礼让,直接用Thread调用方法
			Thread.yield();
		}
	}
}

5)守护线程

概念:将该线程标记为守护线程。被守护的线程执行完毕时,程序即停止运行。守护线程执行完毕不影响被守护线程。

代码实现:

package cn.itcast6;
/*
 * 守护线程
 * public final void setDaemon(boolean on) 设置守护线程
 */
public class Demo {

	public static void main(String[] args) throws InterruptedException {

		MyThread mt = new MyThread("野飞");
		MyThread mt2 = new MyThread("腾飞");
		MyThread mt3 = new MyThread("亦菲");
		
		mt.setPriority(10);
		mt2.setPriority(10);
		mt3.setPriority(1);
		//mt和mt2位守护线程
		mt.setDaemon(true);
		mt2.setDaemon(true);
		
		
		mt.start();
		mt2.start();
		mt3.start();
		
	}

}

6)线程中断

概念:stop方法已过时,通常使用interrupt方法。

代码实现:


package cn.itcast7;
/*
 * 线程中断
 * public final void stop() 已过时   结束线程。杀掉线程!
 * public void interrupt()  中断线程。
 */
public class Demo {

	public static void main(String[] args) throws InterruptedException {

		MyThread mt = new MyThread("野飞");
		MyThread mt2 = new MyThread("腾飞");
		
		mt.start();
		mt2.start();
		
		Thread.sleep(3000);
		//stop已经过时,直接杀死线程
//		mt.stop();
//		mt2.stop();
		//线程中断
		mt.interrupt();
		mt2.interrupt();
	}

}

三、安全问题与lock锁

1)线程的安全问题

概念:在多个线程同时运行时发生的异常情况统称为线程安全问题。线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。

产生原因和前提:

线程随机访问性

有多个线程并行

多个线程有共享数据

多个线程操作了共享数据

处理方式:

使用java提供的同步机制,使某一线程的完整动作执行完毕,其他线程再进行操作

为了保证同步,我们提供了原子性的操作。

同步代码块的格式:

synchronized(锁对象){//该对象可以是任意对象
需要同步的代码;
}

代码实现:

package cn.itcast8;
public class Demo {

	public static void main(String[] args) {
		
		Tickets tickets = new Tickets();
		
		Thread thread = new Thread(tickets,"柳岩");
		Thread thread2 = new Thread(tickets,"花千骨");
		
		thread.start();
		thread2.start();
	}

}

package cn.itcast8;
/*
 * 定义线程执行目标类:票
 */
public class Tickets implements Runnable {

	int number = 100;
	Object lock = new Object();
	
	//重写run方法,卖票的方法
	@Override
	public void run() {
		
		while(true) {
			//使用synchronized将一个原子性操作包裹,是其同步,不同的线程需要使用相同的锁
			synchronized (lock) {
				try {
					//使线程睡眠10毫秒,加大出现安全问题的几率
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//只要有票就可以卖
				if(number>0) {
					//将数字打印即卖票的过程
					System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票");
					//每次卖完一张票可以减少1
					number--;
				}
			}
			
		}
	}

}
同步方法的锁是其所在对象;静态同步方法的锁就是类本身。
package cn.itcast9;
/*
 * 同步方法的锁是其所在对象。
 * 静态同步方法的锁就是类本身。
 */
public class Demo {

	public static void main(String[] args) {
		
		Tickets tickets = new Tickets();
		
		Thread thread = new Thread(tickets,"柳岩");
		Thread thread2 = new Thread(tickets,"花千骨");
		
		thread.start();
		thread2.start();
	}

}

package cn.itcast9;
/*
 * 定义线程执行目标类:票
 */
public class Tickets implements Runnable {

	static int number = 100;
	Object lock = new Object();
	int x = 0;
	
	//重写run方法,卖票的方法
	@Override
	public void run() {
		
		while(true) {
			
			if(x%2==0) {
				synchronized (Tickets.class) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					if(number>0) {
						System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票");
						number--;
					}
				}
			}else {
				method();
			}
			x++;
		}
	}
//	//同步方法:直接在方法声明上加synchronized同步关键字修饰
//	public synchronized void method() {
////		synchronized (lock) {
//			try {
//				Thread.sleep(10);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//			if(number>0) {
//				System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票");
//				number--;
//			}
////		}
//	}
	//静态同步方法:直接在方法声明上加synchronized同步关键字修饰
	public static synchronized void method() {
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if(number>0) {
			System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票");
			number--;
		}
	}
}

另外一种解决同步的解决方案

2)lock锁

package cn.itcast2;
/*
 * Lock锁:
 * 		另外一种同步解决方案
 * 
 * ReentrantLock:Lock接口的子类对象
 * 
 * void lock() 上锁
 * void unlock() 解锁
 */
public class Demo {

	public static void main(String[] args) {
		
		Tickets tickets = new Tickets();
		
		Thread thread = new Thread(tickets,"柳岩");
		Thread thread2 = new Thread(tickets,"花千骨");
		
		thread.start();
		thread2.start();
	}

}

package cn.itcast2;

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

/*
 * 定义线程执行目标类:票
 */
public class Tickets implements Runnable {

	int number = 100;
	Lock lock = new ReentrantLock();
	
	//重写run方法,卖票的方法
	@Override
	public void run() {
		
		while(true) {
			
			lock.lock();
			try {
				//使线程睡眠10毫秒,加大出现安全问题的几率
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//只要有票就可以卖
			if(number>0) {
				//将数字打印即卖票的过程
				System.out.println(Thread.currentThread().getName()+"正在销售第"+ number +"张票");
				//每次卖完一张票可以减少1
				number--;
			}
			lock.unlock();
		}
			
	}

}


3)等待唤醒机制

当出现对同一资源的生产与消费时,可以使用多线程完成对同一资源的操作。而消费者需要等待生产者生产后才能消费,生产者也需要等待消费者消费后才能生产。于是出现了生产者消费者问题。这时可以使用等待唤醒机制完成相关需求。

涉及方法:

涉及并非是Thread类的方法,而是Object类的两个方法:因为锁可以为共享数据本身可以是任意的对象,在runnable中进行等待唤醒当前所在线程。

等待:

public final void wait() throws InterruptedException

让当前线程进入等待状态,如果线程进入该状态,不唤醒或打断,不会解除等待状态。

进入等待状态时会释放锁。

唤醒:

public final void notify()唤醒正在等待的线程。继续等待之后的代码执行。

sleep与wait的区别:

sleep指定时间,wait可指定可不指定。

sleep释放执行权,不释放锁。因为一定可以醒来。

wait释放执行权与锁。

代码实现生产消费交替执行的

package cn.itcast4;

/*
 * 定义共享数据Person类
 */
public class Person {

	private String name;
	private int age;

	public Person() {
		super();
	}

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

}
package cn.itcast4;
/*
 * 生产者的生产执行目标
 */
public class PRunnable implements Runnable {

	//定义Person类型的成员变量,用于传入与其他线程共享的数据
	private Person p;
	
	//定义标志位,用来区别每次赋值的人名与年龄
	int x = 0;
	
	public PRunnable(Person p) {
		this.p = p;
	}

	@Override
	public void run() {

		while(true) {
			synchronized (p) {
				if(x%2==0) {
					p.setName("柳岩");
					p.setAge(32);
				} else {
					p.setName("唐嫣");
					p.setAge(28);
				}
				x++;
			}
		}
	}

}

package cn.itcast4;
/*
 * 消费者的生产执行目标
 */
public class CRunnable implements Runnable {

	//定义Person类型的成员变量,用于传入与其他线程共享的数据
	private Person p;
	
	public CRunnable(Person p) {
		this.p = p;
	}
	
	@Override
	public void run() {
		
		while(true) {
			synchronized (p) {
				System.out.println(p.getName() + ":" + p.getAge());
			}
		}
	}

}

package cn.itcast4;
/*
 * 生产者为一个生产线程  P
 * 消费者为一个消费线程  C
 * 
 * 共享数据:
 * 		一个Person对象
 * 
 * 消费:打印该对象的姓名与年龄
 * 生产:为这个对象的年龄和姓名赋不同的值
 */
public class Demo {

	public static final Object lock = new Object();
	
	public static void main(String[] args) {

		//创建共享数据
		Person p = new Person("唐嫣", 28);
		
		//创建生产者与消费者的线程执行目标类对象
		PRunnable pr = new PRunnable(p);
		CRunnable cr = new CRunnable(p);
		
		//创建线程对象
		Thread PThread = new Thread(pr);
		Thread CThread = new Thread(cr);
		
		//开启线程
		PThread.start();
		CThread.start();
	}

}


4)单列设计模式

内存当中只存在一个实例对象。

单例设计模式要求。

构造方法私有化

定义了自己类型的静态私有成员变量

对外提供公共的、静态的获取实例方法

实例创建方式分为懒汉式与饿汉式

懒汉式时,需要考虑线程安全问题

代码实现:

package cn.itcast3;

import java.io.IOException;

public class Demo {

	public static void main(String[] args) throws IOException {

		Person p = Person.getInstance();
		Person p2 = Person.getInstance();
		
		System.out.println(p==p2);
		
		Runtime runtime = Runtime.getRuntime();
		//notepad记事本
		runtime.exec("notepad");
	}

}

饿汉式

package cn.itcast3;
/*
 * 饿汉式单列设计
 */
public class Person {
	
	//构造方法私有化
	Person(){}
	
	//定义私有静态成员变量
	private static Person p = new Person();
	
	//提供公共静态的方法,用于获取单列的对象  getInstance获取实例化对象
	public static Person getInstance(){
		return p;
	}
	
}

懒汉式

package cn.itcast4;
/*
 * 懒汉式单列设计
 */
public class Person {
	//定义一个锁
	public static Object lock = new Object();

	//构造方法私有化
	private Person(){}
	
	//定义私有静态成员变量
	private static Person p;
	
	//第一种方式
//	//提供公共静态的方法,用于获取单列的对象
//	public static Person getInstance(){
//		synchronized(lock){
//			if(p==null){
//				p = new Person();
//			}
//		}
//		return p;
//	}
	
	
	
	//第二种方式
	//在考虑线程安全的前提下又考虑到了效率问题
//	public static Person getInstance(){
//		
//		if(p==null){
//			synchronized(lock){
//				if(p==null){
//					p=new Person();
//				}
//			}
//		}
//		return p;
//		
//	}
	
	//第三种方式
	public static synchronized Person getInstance(){
		if(p==null){
			p=new Person();
		}
		return p;
		
	}
	
}


相关文章推荐

黑马程序员——学习日记11 java多线程

------- java培训、Android培训 期待与您交流! ---------
  • T2ANDY
  • T2ANDY
  • 2014年04月30日 20:27
  • 355

黑马程序员学习笔记 Java中多线程与并发的总结

1.      计算机系统 使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行;当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了。 ...

黑马程序员_Java多线程

Java多线程 进程     是一个正在执行中的程序。     每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。   线程     就是进程中的一个独立的控制单元...

黑马程序员——Java基础--- 多线程

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 1.多线程:java语言提供并发机制,程序员可以在程序中执行多个线程,每个线程完成一个功能,并...

黑马程序员--java基础--多线程

创建线程 继承Thread类 建线程的第一种方式:继承Thread类. 步骤如下: 1 继承Thread类 2 覆写Thread类中的run方法 3 调用线程的start方法,这个方法有两个作用:  ...

黑马程序员---Java基础学习笔记(多线程-前篇)

------------Android培训、Java培训、期待与您交流---------- 1.进程和线程的定义和区别     进程:正在进行的程序。每一个进程执行都有一个执行顺序,该顺序是一个...

黑马程序员——Java基础__多线程(上)

面向对象——多线程(上) 简单概念:任务管理器——进程里面执行的程序里面就有很多的线程,不是同时执行的,只是CPU在做着快速的切换。 什么是API?就是Java用面向对象的思想给程序员封装...

黑马程序员---java多线程 学习笔记

---------------------- android培训、java培训、期待与您交流! ---------------------- 多线程: 1概念:多线程:允许多个线程并发执行,提高程...

黑马程序员_java基础_多线程

------- android培训、java培训、期待与您交流!--------- 多线程 一、多线程的概述       要理解多线程,就必须理解线程。而要理解线程,就必须知道进程。 ...
  • LFB168
  • LFB168
  • 2015年09月18日 19:22
  • 331

黑马程序员—10—java基础:有关多线程安全的学习笔记和学习心得体会

------- android培训、java培训、期待与您交流! ---------- 1. 通过实例解说多线程安全:    class Ticket implements Runnabl...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:黑马程序员_JAVA之多线程
举报原因:
原因补充:

(最多只允许输入30个字)