黑马程序员---Java中的线程
扫描二维码
随时随地手机看文章
关于Java中的线程
1. 进程
进程:正在运行的程序,所占有内存空间
程序存储在硬盘,运行时期到了内存中
线程:是一个大程序中的子程序
CPU真正执行的是线程,子程序对于CPU来讲独立执行路径,路径就是线程
//======================================================
2. Java中创建线程
任何都是对象,线程也是对象,对象的描述类
java.lang.Thread类
创建线程的第一种方式,继承Thread类,重写run方法
创建Thread子类对象
调用子类中的方法start()开启线程
void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
JVM 本身是单线程的程序,还是多线程的程序
一个线程运行我们写的程序,一个线程不定时收取垃圾,JVM帮助你调用Windows中的功能
为什么继承Thread类,入伙,继承Thread类,子类就是一个线程了
为什么重写run方法 Thread中的run方法是空方法,等待子类重写
线程运行的程序是什么,未知的,Java工程师,开放多线程技术的时候
不知道使用Java编程的人员会运行哪些代码
提供一个标准,就是run方法:不管线程运行哪些代码,必须放在run中,线程就运行你run中的代码
/*
* 创建线程第一种方式,继承Thread类
*/
class Demo1 extends Thread{
public void run(){
for(int x = 0 ; x < 50 ;x++){
System.out.println("run..."+x);
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
while(true){
Demo1 d = new Demo1();
d.start();//开启线程,JVM自动调用线程的run方法
for(int x = 0 ; x < 50 ; x++){
System.out.println("main..."+x);
}
}
}
}
//======================================================
4. 线程的状态图,必须自己会画
5. 线程名字的获取和设置
获取名字,Thread类的方法getName(),返回字符串
线程名字JVM命名的 Thread-0 Thread-1
在Thread子类中,直接使用父类方法getName()获取名字
在不是Thread子类中,获取线程名字
Thread类中,提供了一个静态方法 currentThread()返回值是一个Thread类的对象
方法,返回当前线程对象,既然返回的是对象,方法调用链
String name = Thread.currentThread().getName();
设置线程的名字:
Thread类的方法setName()
Thread类的构造方法
Thread(String name)传递线程名字
Thread子类中,super语句将线程的名字送到父类构造方法
class ThreadName extends Thread{
ThreadName(String name){
super(name);
}
public void run(){
String name =Thread.currentThread().getName();
System.out.println(name+" 线程ThreadName");
}
}
public class ThreadDemo {
public static void main(String[] args) {
//Thread.currentThread();//返回的就是运行main方法的线程对象
String name = Thread.currentThread().getName();
System.out.println(name);
ThreadName t1 = new ThreadName("西班牙0");
ThreadName t2 = new ThreadName("智利2");
// t1.setName("小强");
// t2.setName("旺财");
t1.start();
t2.start();
}
}
//======================================================
6. 售票的一个案例
利用多线程模拟多窗口一起售票
模拟出了,卡机线程,导致了数据的安全问题
多线程操作同一个数据的时候,出现数据安全问题
解决办法:一个线程不执行完毕,其他的线程,不能执行
Java中开放出了同步技术,保证线程的安全性,会阻止其他线程进入
同步代码块
synchronized(任意对象){
线程操作的共享数据
}
/*
* 模拟售票4个窗口一起出售
* 改造成实现Runnable接口的方式
*/
class Ticket implements Runnable{
private int tickets = 100;
private Object obj = new Object();
public void run(){
while(true){
synchronized(obj){
if(tickets > 0){
//线程if判断完毕后,休眠一段时间
try{
Thread.sleep(67);
}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..出售第.."+tickets--);
}
}
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
//创建Ticket对象
Ticket t = new Ticket();
//创建Thread类对象,传递Runnable接口的实现类对象
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//======================================================
7. 创建线程的第二种方式
定义类,实现Runnable接口
重写run方法
class A implements Runnable{
public void run(){
}
}
A类不再是线程类了
直接创建Thread类对象
构造方法Thread(Runnable target) 接受的数据类型是Runnable接口的子类类型
new Thread(new A());
调用Thread类中的start();
两个线程的创建方式的区别
继承Thread类,实现Runnable接口区别
继承,单继承局限性
接口,多现实,避免了单继承的局限性
继承Thread类方式,线程中的数据是对象自己的
实现接口方法,线程中的数据是共享的
写多线程程序推荐使用接口方式
//======================================================
8. 同步的原理
synchronized(任意对象){
线程操作的共享数据
}
对象,写在了同步中
专业名词,对象监视器
通俗一点:锁
线程进到同步代码块后,线程获取这把锁,这扇门永久关闭了
当线程出去同步代码块,将锁释放
厕所原理
多线程操作同一个数据,安全问题
如果是单线程,没有数据安全问题
//======================================================
9. 模拟存钱
一张卡,可以多个柜台存钱
余额是0,每个柜台每次存100元
两个柜台,每个存3次
600元,没存一次,查看余额
同步方法,在方法的声明上写同步关键字
线程每次只有一个运行这个方法
当方法中所有代码都是线程操作的的共享数据
同步方法中,锁是什么
确定的是,锁肯定有,锁必须是对象
锁是本类的对象引用this
方法中的同步代码块,锁直接写this
静态方法中的,同步锁是谁
锁是对象!
静态优先于对象,静态中的锁,是本类的class文件对象
Java中,每一个数据类型,JVM都赋予他们一个静态成员变量
class名字,变量的运行结果就是类的class文件对象
静态方法中的锁,就是本类.class字节码文件对象
/*
* 存钱的时候
* 卡,钱,到银行中去,银行柜台存钱
* 但是,整个Add方法中的所有代码,都是线程操作的共享数据
* 没有必要同步一段代码,同步整个方法
*/
class Bank{
//存钱功能,存一次,看余额
private static int sum = 0;
public static synchronized void add(int money){
// synchronized(Bank.class){
sum = sum + money;
System.out.println(sum);
// }
}
}
class Customer implements Runnable{
private Bank b = new Bank();
public void run(){
for(int x = 0 ; x < 3 ; x++){
Bank.add(100);
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Customer c = new Customer();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
//==============================================================
10. 单例模式
懒汉有安全隐患,多线程并发访问懒汉式的时候
安全问题,同步避免
效率很低,提高懒汉的效率
第一次执行s=null
进同步 创建对象,返回
第二次执行s!=null
没有必要进同步了,直接返回
两次判断,提高效率
class Single{
private Single(){}
private static Single s = null;
public static Single getInstance(){
if(s == null){
synchronized(Single.class){
if( s == null)
s = new Single();
}
}
return s;
}
}
class SingleThead implements Runnable{
public void run(){
for(int x = 0 ; x < 30 ; x++){
Single s = Single.getInstance();
System.out.println(s);
}
}
}
//==============================================================
11. 死锁案例
在多线程的技术中,两个或者两个以上的线程,同时争夺一个对象监视器
导致程序的假死现象
死锁:出现条件,必须多线程,争夺一个锁,程序中,体现在同步代码块的嵌套效果
死锁的案例,面试过程中经常被考到
//==============================================================
12. 线程的通信
两个线程同时对一个资源对象,进行赋值和取值操作
思考,数据安全问题怎么发生的
发生后,怎么解决
数据安全问题:线程的随机性导致程序数据安全隐患
采用了同步技术,依然没有解决
当你发现数据安全问题后,使用了同步技术,还是不能解决
第一点,确定程序是不是多线程在操作共享数据 确定
第二点,使用的同步中的锁,是同一把锁吗,锁不同唯一的
必须将锁变成唯一的对象,才能控制数据安全问题
资源对象,数据的安全性解决了
线程等待和唤醒的方法
没有出现在线程描述类Thread类中
方法定义在了Object类中,为什么这样设计
原因是锁,锁是一个对象,对象是通过类new出来的
锁是哪一个类的对象,无法确定的
但是将方法写在Object类中,所有的类的对象,都具有线程的等待与唤醒方法
wait()方法的使用,将线程永久等待,直到有人唤醒
wait方法必须有锁的支持,wait方法必须写在同步中
锁是唯一的
synchronized(r){
wait();
notify();
}
synchronized(r){
notify()
}
IllegalMonitorStateException
异常,运行时期的异常,抛出该异常,说明wait notify没有锁的支持,没有对象监视器
/*
* 线程通信的代码的优化
*/
//定义资源类,私有处理
class Recource{
private String name;
private String sex;
private boolean flag = false;
//提供get set方法,访问成员变量
public synchronized void set(String name,String sex){
if(flag)
try{
this.wait();
}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void get(){
if(!flag)
try{
this.wait();
}catch(Exception e){}
System.out.println(name+"..."+sex);
flag = false;
this.notify();
}
}
//输入线程
class Input implements Runnable{
private Recource r;
Input(Recource r){this.r = r;}
public void run(){
int x = 0 ;
while(true){
if(x %2 == 0){
r.set("张三", "男");
}else{
r.set("李四", "女");
}
x++;
}
}
}
//输出的线程
class Output implements Runnable{
private Recource r ;
Output(Recource r){this.r=r;}
public void run(){
while(true){
r.get();
}
}
}
public class ThreadDemo5 {
public static void main(String[] args) {
Recource r = new Recource();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
//==============================================================
13. wait() sleep() 导致线程等待,两个方法区别在哪里(不要光看表面。)
sleep(毫秒值)自动醒来
wait()永久等待,需要别的线程唤醒
sleep()方法是Thread类的静态方法
wait()方法是Object类的非静态方法
sleep()不需要对象锁
wait()必须有锁的支持
sleep()方法,执行的时候线程不会释放对象锁
wait()方法,执行的时候,线程放弃对象锁,被唤醒的时候,从新获取对象锁,才能运行
//==============================================================
14. 定时器
Java程序中,有定时功能,按照一定的时间间隔运行指定的程序
定时器类,java.util.Timer
构造方法
Timer(boolean isDaemon) false 不是守护的线程
schedule(TimerTask task, Date firstTime, long period)
定时器任务 第一次时间 间隔
抽象类 TimerTask 时间任务,定时器执行的程序,写在这个类的run方法
/*class Time extends TimerTask{
public void run(){
System.out.println("定时2秒钟发送一次邮件");
}
}*/
public class TimerDemo {
public static void main(String[] args) {
Timer t = new Timer(false);
t.schedule(new TimerTask(){
public void run(){
System.out.println(""定时2秒钟发送一次邮件"");
}
}, new Date(), 3600000);
System.out.println("main...over");
}
}
//==============================================================
15. 多线程通信,生产者与消费者
一个产品,分别线程控制,一个控制生产,一个控制消费
生产一个,消费一个,多生产者,多消费者
多个生产与多个消费,全部的唤醒notifyAll()
唤醒以后,数据安全性还是没解决
线程在wait上等待,被唤醒的时候,从Wait这里起来
起来以后,不会再次判断flag是true,还是false,因此数据问题,没哟解决
线程,被唤醒以后,但是判断标记!!
用的是循环的方式,解决线程倒下去后,再起来,必须还要判断标记
但是发现一个问题:
notifyAll()唤醒了全部等待的线程
1个活的,7个等着,全部醒来
浪费资源,能不能唤醒对方的一个线程的
生产者,只唤醒一个消费者
消费者,只唤醒一个生产者
唤醒本方是没有意义,全部唤醒是浪费资源的
Java的JDK1.4版本之前,是做不到了
到了JDK1.5版本后,就可以实现了
提供一套新的多线程的操作方式
synchronized,被替换了
wait(),notify(),notifyAll(),被替换了
JDK1.5的新特性,线程锁定操作
导包 java.util.concurrent.locks
//==============================================================
16. JDK1.5的锁 lock接口
Lock接口--synchronized同步
同步是有锁提供
接口中,定义了两个方法 lock() unlock()
要同步线程的时候,使用这两个方法,进行锁操作
Lcok接口的实现类对象ReentrantLock
获取到接口的实现类对象,调用方法,锁操作
新的技术中,JDK提供了一个接口Condition
替换了原有线程方法,wait,notify
将线程进行分组管理
t1-t4 set方法,用锁就是set方法中的锁
t5-t8 get方法,用锁就是get方法中的锁
一个lock接口上,可以实现多个Condition对象
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
将一个接口Lock,分成两组管理
Condition接口中的三个方法
await() -- wait()
signal() -- notify()
signalAll() -- notifyAll();
/*
* 将案例,改造成lock锁方式实现功能
*/
import java.util.concurrent.locks.*;
class Product{
private String name;
//定义计数器
private int count ;
//定义标识
private boolean flag = false;
//定义Lock锁对象
private Lock lock = new ReentrantLock();
//通过Lock接口,获取Condition对象
private Condition pro = lock.newCondition();
private Condition cus = lock.newCondition();
//定义生产方法
public void set(String name){
//获取锁
lock.lock();
while(flag)
try{
pro.await();
}catch(Exception e){}
this.name = name + count++;
System.out.println(Thread.currentThread().getName()+"生产第..."+this.name);
flag = true;
//this.notifyAll();
//释放锁
cus.signal();
lock.unlock();
}
//定义消费方法
public void get(){
lock.lock();
while(!flag)
try{
cus.await();
}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"消费第........."+this.name);
flag = false;
//this.notifyAll();
pro.signal();
lock.unlock();
}
}
//定义生产者
class Producter implements Runnable{
private Product p ;
Producter(Product p){this.p = p;}
public void run(){
while(true){
p.set("键盘");
}
}
}
//定义消费这
class Customer implements Runnable{
private Product p ;
Customer(Product p){this.p = p;}
public void run(){
while(true){
p.get();
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Product p = new Product();
Producter producter = new Producter(p);
Customer customer = new Customer(p);
Thread t1 = new Thread(producter);
Thread t2 = new Thread(producter);
Thread t3 = new Thread(producter);
Thread t4 = new Thread(producter);
Thread t5 = new Thread(customer);
Thread t6 = new Thread(customer);
Thread t7 = new Thread(customer);
Thread t8 = new Thread(customer);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
t8.start();
}
}
//==========================================================
17. 线程的停止方式
Thread类中,有一个方法stop(),过时
终止线程的运行,目的结束run方法就行
停止线程的第一种方式,改变变量的值,结束while循环,结束了run方法
处于等待中的线程,怎么停下
例子:
我有一个朋友,失眠,找了一个催眠大师(水平很高)
进行了催眠,朋友就睡了(wait())
催眠师说,被我催眠的人,只有我能叫醒
催眠师死了,不能让朋友永久的等待下去
拍你一板砖,醒来,收到了伤害(异常)
线程的第二种停止方式
void interrupt() + 异常停下,等待中的线程
打击线程方法,处于等待的线程,将会抛出异常
/*
* 线程如何停止下来
*/
class StopThread implements Runnable{
private boolean flag = true;
public void run(){
while(flag){
synchronized(this){
try{this.wait();}catch(Exception e){
e.printStackTrace();
//System.out.println(e.getMessage()+"打你一板砖");
flag = false;
}
System.out.println("run....");
}
}
}
public void setFlag(boolean flag){
this.flag = flag;
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t = new Thread(st);
t.start();
for(int x = 0 ; x < 1000 ; x++){
if(x==999)
//st.setFlag(false);
t.interrupt();
else
System.out.println("main"+x);
}
}
}
//==========================================================
18. 守护线程
Thread类中。setDaemon(boolean )传递是true,线程守护线程
如果所有运行的线程,都是守护线程,JVM退出
方法,必须在start开始前调用
Feiq,开启多个聊天窗口的时候,一旦关闭飞秋主程序,聊天窗口也就关闭了
聊天窗口线程,就是飞秋主线程的守护线程,一旦主线程死亡,所有的守护线程就死亡
/*
* 守护线程
*/
class ThreadDaemon implements Runnable{
public void run(){
while(true)
System.out.println("run....");
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
ThreadDaemon td = new ThreadDaemon();
Thread t = new Thread(td);
t.setDaemon(true);
t.start();
}
}
//==========================================================
19. Thread中其他方法,toString() setPriority() join() yield()
toString()继承Object,重写toString()
Thread[Thread-0,5,main]
5 优先级,main 线程组
优先级三个级别,最低,默认,最高
setPriority(int )设置优先级
但是优先级的效果,多核,多线程的CPU上,效果不是很明显了
join() 加入 等待该线程终止
使用join方法的线程,不停止,其他线程运行不了
static yield()
/*
* 线程的让步方法,static yield
*/
class YieldThread implements Runnable{
public void run(){
for(int x = 0 ; x < 100 ; x++){
Thread.yield();
System.out.println(Thread.currentThread().getName()+"run.."+x);
}
}
}
public class ThreadDemo5 {
public static void main(String[] args) {
YieldThread yt = new YieldThread();
Thread t0 = new Thread(yt);
Thread t1 = new Thread(yt);
t0.start();
t1.start();
}
}
/*
* 等待该线程终止
*/
class JoinThread implements Runnable{
public void run(){
for(int x = 0 ; x < 100; x++){
System.out.println(Thread.currentThread().getName()+"run.."+x);
}
}
}
public class ThreadDemo4 {
public static void main(String[] args) throws Exception{
JoinThread jt = new JoinThread();
Thread t0 = new Thread(jt);
Thread t1 = new Thread(jt);
t0.start();
t0.join();
t1.start();
for(int x = 0 ;x < 100 ; x++){
System.out.println("main...."+x);
}
}
}
/*
* Thread中的toString()
*/
class ThreadToString implements Runnable{
public void run(){
for(int x = 0 ; x < 100 ; x++){
System.out.println(Thread.currentThread().getName()+"run.."+x);
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
ThreadToString tts = new ThreadToString();
Thread t0 = new Thread(tts);
Thread t1 = new Thread(tts);
t0.setPriority(Thread.MAX_PRIORITY);
t1.setPriority(Thread.NORM_PRIORITY);
t0.start();
t1.start();
// System.out.println(t);
}
}