黑马程序员之多线程



------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一、概述

       1、进程:正在执行的程序,每一个进程都会有一个执行顺序,称为执行单元。

       2、线程:进程中包含线程,是进程中的一个独立的执行单元,一个在执行的进程至少有一个线程。

       3、多线程:就是一个程序能够执行多个任务。这些任务都是由里面的线程来执行的。如:JVM执行的时候就是一个多线程的,里面包至少包含主线程和垃圾回收的线程。多线程的存在大大的提高了程序的效率。


二、创建线程

       可以通过两种方式来创建线程。

       1、继承Thread类, 并重写run()方法, 调用start()方法来开启线程

       2、实现Runnable接口, 并重写run()方法, 开启的方法是:创建实现类的对象,new Thread(实现类对象).start()

       3、俩者的区别:
             避免了JAVA单继承的局限性, 在定义线程的时候尽量使用实现的方式
             实现Runnable接口: 代码存放在实现接口的类的run()方法中
             继承Tread类: 代码存放在Tread类的子类的run()方法中

       4、示例

class Test extends Thread{
	@Override
	public void run() {
		System.out.println("使用继承方式创建线程");
	}
}

class Test2 implements Runnable{

	@Override
	public void run() {
		System.out.println("使用实现接口方式创建线程");
	}
}

public class TheardDemo {

	public static void main(String[] args) {

		//使用继承方式创建线程的开启方式
		Test t = new Test();
		t.start();
		
		//使用实现接口的方式创建线程的开启方式
		Test2 t2 = new Test2();
		Thread thread = new Thread(t2);
		thread.start();
		
	}
}

三、线程的状态

四、多线程同步

       当多条语句操作同一线程共享数据时,一个线程对多条语句只执行到一部分代码,而另外一个线程就参与进来执行,导致共享

       数据错误。

       解决:当多个线程操作同一个数据时,让一个线程先执行完,再执行其他的线程

       synchronized(){

             需要同步的代码

       }

       同步的两个前提:1、必须有两个或者两个以上的线程;2、必须是多个线程使用同一个锁

       优缺点:优点:解决多线程安全问题;缺点:多线程需要判断锁,较为消耗资源

class Demo2 implements Runnable {

	private int t = 100;

	Object obj = new Object();
	@Override
	public void run() {

		while (true) {
			synchronized (obj) {
				try {
					Thread.sleep(10);
				} catch (Exception e) {
				}

				if (t <= 0)
					return;
				System.out.println(Thread.currentThread().getName() + " -> " + t--);
			}
		}
	}
}



        同步函数,注意:同步函数使用的锁为this

        在函数上加上关键字synchronized,示例:

class MyTicket implements Runnable {

	private int tick = 100;
	
	//同步函数
	public synchronized void show() {
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println(Thread.currentThread().getName() + " -> " + tick--);
	}

	@Override
	public void run() {

		while(tick>0){
			show();
		}
	}
}


        静态函数同步,注意静态函数同步的锁是对象文件的class

        示例:

//懒汉式
class Single{
	
	private static Single s = null;
	private Single(){
		
	}
	
	public static Single getInstance(){
		
		if(null==s){
			synchronized(Single.class){
				if (null==s) {
					s=new Single();
				}
			}
		}
		return s;
	}
}


五、线程死锁

       就是同步中嵌套同步。

       示例:

class MyTest implements Runnable {

	private boolean flag;

	public MyTest(boolean flag) {
		this.flag = flag;
	}

	@Override
	public void run() {

		if (flag) {

			synchronized (MyLock.lockA) {
				System.out.println("if lockA");
				synchronized (MyLock.lockB) {
					System.out.println("if lockB");
				}
			}

		} else {
			synchronized (MyLock.lockB) {
				System.out.println("else lockB");
				synchronized (MyLock.lockA) {
					System.out.println("else lockA");
				}
			}
		}
	}
}

class MyLock {
	static Object lockA = new Object();
	static Object lockB = new Object();
}

public class DeadLockTest {

	public static void main(String[] args) {
		new Thread(new MyTest(true)).start();
		new Thread(new MyTest(false)).start();
	}
}


六、线程间通讯

       多个线程同时操作同一块资源,只是操作的方式不同

      

class Res{
	public String name;
	public String sex;
	public boolean flag = false;
}

class Input implements Runnable{
	
	private Res res;
	public Input(Res res) {
		this.res=res;
	}

	@Override
	public void run() {
		int f =0;
		
		while (true) {
			synchronized (res) {
				
				if(res.flag)
					try {
						res.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				
				if(f == 0){
					res.name="tom";
					res.sex="man";
				}else{
					res.name="吉米";
					res.sex="女";
				}
				f=(f+1)%2;
				res.flag=true;
				res.notify();
			}
			
		}
	}
}

class Output implements Runnable{

	private Res res;
	
	public Output(Res res) {
		this.res=res;
	}
	
	@Override
	public void run() {
		
		
		while(true){
			
			synchronized (res) {
				if(!res.flag)
					try {
						res.wait();	//等待, 放弃CPU执行权, 释放锁
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				System.out.println(res.name + " -----> " + res.sex);
				res.flag=false;
				res.notify();	//唤醒
			}
		}
	}
}


public class InputOutputDemo {

	public static void main(String[] args) {

		Res res = new Res();
		Input in = new Input(res);
		Output out = new Output(res);
		new Thread(in).start();
		new Thread(out).start();
	}
}


//notifyAll()  唤醒等待池的所有线程
//wait()和notify()  都是要对持有监视器(锁)的线程进行操作, 都是object中的方法, 因为锁可以是任意对象, 所以这个方法定义在最上层的类中
//等待和唤醒必须在同一个锁中进行.


七、停止线程

       在JDK1.5以前有stop()方法可以用来停止线程, 但在这个版本之后就舍弃了这个 方法。开启多线程运行, 通常代码都是循环的,   所以只要控制循环, 就可以让run()方法结束, 就可以让线程结束 线程同步时, 可能挺不下来, 解决如下
特殊情况: 当线程处于冻结状态, 就不会读取标记, 线程就不会结束
但没有指定方式让冻结的线程恢复到运行状态时, 这时需要对冻结进行清除, 强制让线程恢复到运行中来, 这样就可以操作标记让线程结束
Thread中提供了该方法, iterrupt()

示例:

class StopThread implements Runnable{

	private boolean flag = true;
	
	@Override
	public synchronized void run() {
		while(flag){
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println(Thread.currentThread().getName() + " -> Excption");
				flag = false;
			}
			System.out.println(Thread.currentThread().getName() + " <---");
		}
	}
	
	public void changeFlag(){
		this.flag = false;
	}
}


public class StopThreadDemo {

	public static void main(String[] args) {

		StopThread s1 = new StopThread();
		
		Thread t1 = new Thread(s1);
		Thread t2 = new Thread(s1);
		
		//设置为守护线程, setDaemon()方法必须在开启线程之前调用
		//当设置为守护线程的时候, 主线程结束, 那么守护线程也随之结束
		t1.setDaemon(true);
		t2.setDaemon(true);
		
		
		t1.start();
		t2.start();
		
		int num = 0;
		
		while(true){
			
			
			if(num++ == 60){
				//s1.changeFlag();
				t1.interrupt();
				t2.interrupt();
				break;
			}
			
			System.out.println(Thread.currentThread().getName() + " -> " + num);
		}
		
		System.out.println("over");
	}
}


     yield()方法和join()方法,示例:

class Demo3 implements Runnable{

	@Override
	public void run() {

		for(int i=0;i<40;i++){
			System.out.println(Thread.currentThread().getName() + " -> " + i);
			
			Thread.yield();	//暂停当前正在执行的对象, 并执行其他的线程
		}
	}
}



public class JoinDemo {

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

		Demo3 d = new Demo3();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		
		t1.start();
		t1.join();	//当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
		
		t2.start();
		
		for(int i = 0;i<80;i++){
			System.out.println("main -> " + i);
		}
		System.out.println("over");
	}
}


黑马程序员多线程练习题主要包括两个问题。第一个问题是如何控制四个线程在打印log之前能够同时开始等待1秒钟。一种解决思路是在线程的run方法中调用parseLog方法,并使用Thread.sleep方法让线程等待1秒钟。另一种解决思路是使用线程池,将线程数量固定为4个,并将每个调用parseLog方法的语句封装为一个Runnable对象,然后提交到线程池中。这样可以实现一秒钟打印4行日志,4秒钟打印16条日志的需求。 第二个问题是如何修改代码,使得几个线程调用TestDo.doSome(key, value)方法时,如果传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果。一种解决方法是使用synchronized关键字来实现线程的互斥排队输出。通过给TestDo.doSome方法添加synchronized关键字,可以确保同一时间只有一个线程能够执行该方法,从而实现线程的互斥输出。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [黑马程序员——多线程10:多线程相关练习](https://blog.csdn.net/axr1985lazy/article/details/48186039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值