黑马程序员_多线程及单例模式
来源:互联网 发布:路由器对网络稳定性 编辑:程序博客网 时间:2024/05/20 20:48
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
进程与线程:
进程,正在运行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径
,或者叫一个控制单元。
线程,就是进程中的一个独立的控制单元。线程在控制着进程的执行。一个进程中至少有一个线程。
其实更细节说明jvm启动不止一个线程,还有负责垃圾回收机制的线程。
线程都有自己的默认名称,也可以通过setName()方法,或者通过构造方法来给线程设置自定义的名称。
启动线程的两种方式:
第一种方式:
继承Thread类:
1、定义类继承Thread。
2、复写Thread类中的run方法。目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的start方法,该方法两个作用:启动线程,调用run方法。
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。
- class Demo extends Thread {
- @Override
- public void run() { //重写Thread的run()方法
- System.out.println("demo run");
- }
- }
- public static void main(String[] args) throws IOException {
- Demo demo = new Demo();
- //开启线程的同时并执行该线程下的run方法,run方法并不能启动一个新的线程。
- demo.start();
- }
第二种方式:
实现Runnable接口:
1、定义类实现Runnable接口
2、覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
3、通过Thread类建立线程对象。
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去执行对象的run方
法。就必须明确该run方法所属对象。
5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
- public class Demo {
- public static void main(String[] args) throws IOException {
- Demo d = new Demo();
- Thread t1 = new Thread(d);//创建线程,并将Runnable接口的子类对象传递给Thread
- t1.start();//启动线程
- }
- }
- class Demo implements Runnable {
- @Override
- public void run() {//实现接口的run方法
- System.out.println("demo run");
- }
- }
两种方式区别:
1、实现方式的好处:避免了单继承的局限性;在定义线程时,建议使用实现方式。
2、继承方式的线程代码存放在Thread子类的run方法中。
3、实现方式的线程代码存放在接口的子类的run方法中。
线程状态分析图:
多线程的安全问题:
导致安全问题的出现的原因:
1、多个线程访问出现延迟。
2、线程随机性。
同步(synchronized):
1、同步代码块:
synchronized(对象)
{
需要同步的代码;
}
2、同步函数
1、同步函数的锁是this。
2、如果同步函数被静态修饰,则锁为Class。
同步的前提:
1、同步需要两个或者两个以上的线程。
2、多个线程使用的是同一个锁。
未满足这两个条件,不能称其为同步。
同步的弊端:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的
运行效率。
- class Ticket implements Runnable
- {
- private int tick = 1000;
- Object obj = new Object();//锁对象,锁可以是任何对象,任何对象又都继承于Object
- public void run()
- {
- while(true)
- {
- synchronized(obj)//同步代码块
- {
- if(tick>0)
- {
- System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
- }
- }
- }
- }
- }
同步中出现的死锁现象:
两把锁嵌套使用则有可能出现死锁。
- class Test implements Runnable
- {
- private boolean flag;//标志位
- Test(boolean flag)//构造方法中传入一个标志初始化值
- {
- this.flag = flag;
- }
- public void run()
- {
- if(flag)//如果flag为true,则进入同步代码块,拿到锁locka,若此时另一个线程已经开始运行,并flag的值为false,则拿到了锁lockb
- { // 当拿有locka锁的线程要进入b锁的代码块时,lockb却被另一个线程持有,而此时持有lockb锁的线程也在等待locka锁,这样发生嵌套的情况,
- //即会发生死锁情况。
- while(true)
- {
- synchronized(MyLock.locka)
- {
- System.out.println(Thread.currentThread().getName()+"...if locka ");
- synchronized(MyLock.lockb)
- {
- System.out.println(Thread.currentThread().getName()+"..if lockb");
- }
- }
- }
- }
- else
- {
- while(true)
- {
- synchronized(MyLock.lockb)
- {
- System.out.println(Thread.currentThread().getName()+"..else lockb");
- synchronized(MyLock.locka)
- {
- System.out.println(Thread.currentThread().getName()+".....else locka");
- }
- }
- }
- }
- }
- }
- class MyLock
- {
- static Object locka = new Object();//定义locka锁对象
- static Object lockb = new Object();//定义lockb锁对象
- }
- class DeadLockTest
- {
- 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 IOtest1 {
- public static void main(String[] args) throws InterruptedException {
- /**
- * 创建4个线程同时执行,其中两个生产者和两个消费者
- * 但商品依然是生产一个消费一个的方式进行
- */
- Resource r = new Resource();
- Producer pro = new Producer(r);
- Consumer con = new Consumer(r);
- Thread t1 = new Thread(pro);
- Thread t2 = new Thread(con);
- Thread t3 = new Thread(pro);
- Thread t4 = new Thread(con);
- //启动这4个线程
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- }
- }
- class Resource {
- private String name;
- private int count = 1;
- private boolean flag = false;
- /**
- * 资源生产方法
- * @param name
- */
- public synchronized void set(String name) {
- while(flag){ //if和while区别:当等待中的线程再次被唤醒时,if不需要再次判断标志位,直接向下执行,而while需要重新判断标志位
- try {
- this.wait();//线程等待
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- this.name = name + "..." + count++;
- System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);//打印生产
- flag = true;
- this.notifyAll(); //notify和notifyAll方法:前者只能唤醒先进入线程池中的,并且持有该锁的线程,而后者可以唤醒线程池中所有持有该锁的线程
- }
- /**
- * 资源消费方法
- */
- public synchronized void out() {
- while(!flag) {
- try {
- this.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(Thread.currentThread().getName()+"...消费者"+this.name);//打印消费
- flag = false;
- this.notifyAll();
- }
- }
- /**
- * 生产者类
- * @author Administrator
- *
- */
- class Producer implements Runnable {
- private Resource res;
- Producer(Resource res) {
- this.res = res;
- }
- @Override
- public void run() {
- while(true) {
- res.set("-商品-");//调用生产方法
- }
- }
- }
- /**
- * 消费者类
- * @author Administrator
- *
- */
- class Consumer implements Runnable {
- private Resource res;
- Consumer(Resource res) {
- this.res = res;
- }
- @Override
- public void run() {
- while(true) {
- res.out();//调用消费方法
- }
- }
- }
wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中:
1,这些方法存在与同步中。
2,使用这些方法时必须要标识所属的同步的锁。
3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
wait(),sleep()有什么区别:
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
停止线程:
只有run方法结束后,线程便会停止。
开启多线程运行,运行代码通常是循环结构,只要控制好循环,就可以让线程停止。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();
守护线程:
1.5版本JDK中的线程锁:
- <strong>private Lock lock = new ReentrantLock();
- Condition con = lock.newCondition();
- </strong>
一个Lock中可以有多个Condition。
单例设计模式:
单例设计模式:解决一个类在内存只存在一个对象。
保证对象唯一:
1、为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象
2、还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
3、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
代码体现过程:
1、将构造函数私有化。
2、在类中创建一个本类对象。
3、提供一个方法可以获取到该对象。
饿汉式:
- private static Single s = new Single();
- private Single() {}
- public static Single getInstance() {
- return s;
- }
懒汉式(解决懒汉式线程安全问题):
- class Single {
- private static Single s = null;//延迟加载
- private Single() {}
- public static Single getInstance() {
- if(s == null) {//为了提高运行效率,在判断锁之前再判断一次对象是否存在
- synchronized(Single.class) {
- if(s == null) {
- s = new Single();
- }
- }
- }
- return s;
- }
- }
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
- 黑马程序员_多线程及单例模式
- 黑马程序员_多线程及单例模式
- 黑马程序员_七、多线程及String类和单例设计模式
- 黑马程序员_单例设计模式&多线程
- 黑马程序员_静态及单例设计模式
- 黑马程序员_单例模式及Java实现
- 黑马程序员--Java基础--多线程创建及单例模式
- 黑马程序员_多线程的死锁和多线程下的单例设计模式
- 黑马程序员_单例设计模式
- 黑马程序员_单例设计模式
- 黑马程序员_单例设计模式
- 黑马程序员_单例设计模式
- 黑马程序员_单例模式
- 黑马程序员_单例模式
- 黑马程序员_单例设计模式
- 黑马程序员_单例设计模式
- 黑马程序员_单例设计模式
- 黑马程序员_单例设计模式
- poj-3264-Balanced Lineup-线段树-区域查询
- 数字图像处理的就业前景
- JAVA的基本数据类型和引用数据类型的区别
- Linux下的mysql命令
- oracle安装
- 黑马程序员_多线程及单例模式
- 日本人的选择
- 变量类型
- hdu-1698-Just a Hook-线段树-区域更新,区域查询
- treeview绑定xml数据
- LeetCode: Merge Intervals [055]
- Maven创建Mina项目的目录
- Android恢复出厂设置流程分析【Android源码解析十】
- android使用XML-----DOM