Java自学笔记Day24

Day24(线程)

多线程

多线程的引入

A:什么是线程
	*线程就是程序执行的一条路径,一个进程中可以包括多条线程
	*多线程并发执行可以提高程序的效率,可以同时完成多项工作
B:多线程的应用场景
	*红蜘蛛同时共享屏幕给多个电脑
	*迅雷开启多线程同时下载
	*QQ同时和多人一起视频

多线程并行和并发的区别

*并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在运行,如果需要并行运行,需要多核	cpu	
*并发就是几个任务在同一时间段内同时运行

Java程序运行原理和JVM的启动是多线程吗?

A:JAVA程序运行原理
	*Java命令会启动Java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程,该进程会主动启动一个"主线程",然后主线程会去调用一个main方法
B:JVM的启动是多线程的吗?
	JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的

多线程程序的实现方式

多线程程序实现方式1
A:将类声明成Thread的子类,然后重写run()方法

    public class Demo2_Thread {

        public static void main(String[] args) {
            MyThread mt = new MyThread();		//4,创建Thread类的子类对象
            mt.start();							//5,开启线程

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

    }

    class MyThread extends Thread {				//1,继承Thread
        public void run() {						//2,重写run方法
            for(int i = 0; i < 1000; i++) {		//3,将要执行的代码写在run方法中
                System.out.println("aaaaaaaaaaaa");
            }
        }
    }
多线程实现方式2
A:创建线程的另一种方式声明实现Rannable接口的类.该类然后实现run方法.
    
    public class Demo3_Thread {
		public static void main(String[] args) {
            MyRunnable mr = new MyRunnable();	//4,创建Runnable的子类对象
            //Runnable target = mr;	
            Thread t = new Thread(mr);			//5,将其当作参数传递给Thread的构造函数
            t.start();							//6,开启线程

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

    }

    class MyRunnable implements Runnable {		//1,定义一个类实现Runnable

        @Override
        public void run() {						//2,重写run方法
            for(int i = 0; i < 1000; i++) {		//3,将要执行的代码写在run方法中
                System.out.println("aaaaaaaaaaaa");
            }
        }

    }
实现Runnable的原理
*查看原码
	*1.看Thread类的构造函数,传递了Runnable接口的引用
	*2.通过init()方法找到传递的target给成员变量的target赋值
	*3.查看run方法,发现run方法中有判断,如果target不为null就会用Runnable接口子类对象的run方法

两种方式的区别
*查看源码
	*1.继承Thread:由于子类重写了Thread类的run(),当调用start(),直接找子类的run()方法;
	*2.实现Runnable:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空,不为空时编译时看的是Runnable的run()方法,运行时执行的是子类的run()方法;
继承Thread类
	*好处是:可以直接使用Thread类中的方法,代码简单
	*弊端是:如果已经有了父类,就不能用这种方法
实现Runnable接口
	*好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口可以多实现的
	*弊端是:不能直接使用Thread中的方法,需要先获取线程对象后,才能得到Thread的方法,代码复杂	

匿名内部类实现多线程的两种方式

*继承Thread

		new Thread() {									//1,继承Thread类
			public void run() {							//2,重写run方法
				for(int i = 0; i < 1000; i++) {			//3,将要执行的代码写在run方法中
					System.out.println("aaaaaaaaaaaaaa");
				}
			}
		}.start();											//4,开启线程
		
*实现Runnable接口

	new Thread(new Runnable() {				//1,将Runnable的子类对象传递给Thread的构造方法
			public void run() {							//2,重写run方法
				for(int i = 0; i < 1000; i++) {			//3,将要执行的代码写在run方法中
					System.out.println("bb");
				}
			}
		}).start();											//4,开启线程

多线程中的一些方法

获取和设置线程对象的名字
A:获取名字
	*通过getName()方法获取线程对象的名字;
B:设置名字:
	*通过构造函数可以传入String类型的名字
	public static void demo1() {
		new Thread("王五") { // 通过构造方法给name赋值
			@Override
			public void run() {
				System.out.println(this.getName() + "....aaaaaaaaa");
			}
		}.start();

		new Thread("赵六") {
			@Override
			public void run() {
				System.out.println(this.getName() + "....bb");
			}
		}.start();
	}
	*通过setName(String)方法这只线程对象名字
	Thread t1 = new Thread() {
			@Override
			public void run() {
				// this.setName("张三");
				System.out.println(this.getName() + "....aaaaaaaaaaaaa");
			}
		};

		Thread t2 = new Thread() {
			@Override
			public void run() {
				// this.setName("李四");
				System.out.println(this.getName() + "....bb");
			}
		};

		t1.setName("张三");
		t2.setName("李四");
		t1.start();
		t2.start();
获取当前线程对象
*Thread.currentThread(),//主线程也可以获取

    public static void main(String[] args) {
            new Thread() {
                public void run() {
                    System.out.println(getName() + "....aaaaaa");
                }
            }.start();


            new Thread(new Runnable() {
                public void run() {
                    //Thread.currentThread()获取当前正在执行的线程
                    System.out.println(Thread.currentThread().getName() + "...bb");
                }
            }).start();

            Thread.currentThread().setName("我是主线程");
            System.out.println(Thread.currentThread().getName());
       }	
休眠线程
A:Thread.sleep(毫秒,纳秒) //控制当前线程休眠若干毫秒
	public static void demo1() throws InterruptedException {
		for(int i = 20; i >= 0; i--) {
			Thread.sleep(1000);
			System.out.println("倒计时第" +i + "秒");
		}
	}
守护线程
setDaemon(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程执行都执行结束后,自动退出

		Thread t1 = new Thread() {
			public void run() {
				for(int i = 0; i < 2; i++) {
					System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaa");
				}
			}
		};
		
		Thread t2 = new Thread() {
			public void run() {
				for(int i = 0; i < 50; i++) {
					System.out.println(getName() + "...bb");
				}
			}
		};
		
		t2.setDaemon(true);							//设置为守护线程
		
		t1.start();
		t2.start();
加入线程
*join(),当前线程暂停,等待执行的线程执行结束后,当前线程再继续执行
*join(int),可以等待指定的毫秒后继续执行
	final Thread t1 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					System.out.println(getName() + "...aaaaaaaaaaaaa");
				}
			}
		};

		Thread t2 = new Thread() {
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					if (i == 2) {
						try {
							t1.join();
							// t1.join(1); //插队指定的时间,过了指定时间后,两条线程交替执行
						} catch (InterruptedException e) {

							e.printStackTrace();
						}
					}
					System.out.println(getName() + "...bb");
				}
			}
		};

		t1.start();
		t2.start();
礼让线程
*yield让出CPU	//看不到什么效果
设置线程的优先级
*setPrority
同步代码块
A:什么情况下需要同步代码块?
	当多线程并发,有多段代码同时执行时,我们希望某一段代码执行过程中CPU不要切换到其他线程工作,这时就需要同步
B:同步代码块
	*使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块
	*多个同步代码块如果使用相同的锁对象,那么他们就是同步的
	
public class Demo1_Synchronized {

	/**
	 * @param args
	 * 同步代码块
	 */
	public static void main(String[] args) {
		final Printer p = new Printer();

		new Thread() {
			@Override
			public void run() {
				while (true) {
					p.print1();// 匿名内部类中使用成员变量,必须用final修饰
				}
			}
		}.start();

		new Thread() {
			@Override
			public void run() {
				while (true) {
					p.print2();
				}
			}
		}.start();
	}

}

class Printer {
	Demo d = new Demo();

	public void print1() {
		// synchronized (new Demo()) { // 同步代码块,锁机制,锁对象可以是任意的
		synchronized (d) {
			System.out.print("好");
			System.out.print("好");
			System.out.print("学");
			System.out.print("习");
			System.out.print("\r\n");
		}

	}

	public void print2() {
		// synchronized (new Demo()) { // 锁对象不能用匿名对象,因为匿名对象不是同一个对象
		synchronized (d) {
			System.out.print("天");
			System.out.print("天");
			System.out.print("向");
			System.out.print("上");
			System.out.print("\r\n");
		}
	}
}

class Demo {
}	
同步方法
// 非静态的同步方法的锁对象是什么?
	// 答:非静态的同步方法的锁对象是this
	// 静态的同步方法的锁对象是什么?
	// 是该类的字节码对象
	
class Printer2 {
	Demo d = new Demo();

	// 非静态的同步方法的锁对象是什么?
	// 答:非静态的同步方法的锁对象是this
	// 静态的同步方法的锁对象是什么?
	// 是该类的字节码对象
	public static synchronized void print1() { // 同步方法只需要在方法上加synchronized关键字即可
		System.out.print("好");
		System.out.print("好");
		System.out.print("学");
		System.out.print("习");
		System.out.print("\r\n");
	}

	public static void print2() {
		// synchronized(new Demo()) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象
		synchronized (Printer2.class) {
			System.out.print("天");
			System.out.print("天");
			System.out.print("向");
			System.out.print("上");
			System.out.print("\r\n");
		}
	}
}

线程安全

*多线程并发操作同一数据时,就有可能出现线程安全问题
*使用同步技术可以解决这种问题,把操作数据的代码进行同步,不要多个线程一起操作

public class Demo3_Ticket {

	/**
	 * 需求:铁路售票,一共100张,通过四个窗口卖完.
	 */
	public static void main(String[] args) {
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
	}

}

class Ticket extends Thread {
	private static int ticket = 100;

	// private static Object obj = new Object(); //如果用引用数据类型成员变量当作锁对象,必须是静态的
	@Override
	public void run() {
		while (true) {
			synchronized (Ticket.class) {
				if (ticket <= 0) {
					break;
				}
				try {
					Thread.sleep(10); // 线程1睡,线程2睡,线程3睡,线程4睡
				} catch (InterruptedException e) {

					e.printStackTrace();
				}
				System.out.println(getName() + "...这是第" + ticket-- + "号票");
			}
		}
	}
}
public class Demo4_Ticket {

	/**
	 * @param args
	 * 火车站卖票的例子用实现Runnable接口
	 */
	public static void main(String[] args) {
		MyTicket mt = new MyTicket();
		new Thread(mt).start();
		new Thread(mt).start();
		new Thread(mt).start();
		new Thread(mt).start();
		
		/*Thread t1 = new Thread(mt);				//多次启动一个线程是非法的
		t1.start();
		t1.start();
		t1.start();
		t1.start();*/
	}

}

class MyTicket implements Runnable {
	private int tickets = 100;
	@Override
	public void run() {
		while(true) {
			synchronized(this) {
				if(tickets <= 0) {
					break;
				}
				try {
					Thread.sleep(10);				//线程1睡,线程2睡,线程3睡,线程4睡
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");
			}
		}
	}
}

多线程死锁

*多线程同步的时候,如果同步代码嵌套,使用相同锁,就有可能出现死锁
	*尽量不要嵌套使用
	
public class Demo5_DeadLock {

	/**
	 * @param args
	 */
	private static String s1 = "筷子左";
	private static String s2 = "筷子右";

	public static void main(String[] args) {
		new Thread() {
			public void run() {
				while(true) {
					synchronized(s1) {
						System.out.println(getName() + "...获取" + s1 + "等待" + s2);
						synchronized(s2) {
							System.out.println(getName() + "...拿到" + s2 + "开吃");
						}
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					synchronized(s2) {
						System.out.println(getName() + "...获取" + s2 + "等待" + s1);
						synchronized(s1) {
							System.out.println(getName() + "...拿到" + s1 + "开吃");
						}
					}
				}
			}
		}.start();
	}
}	

线程安全的类回顾

Vector,StringBuffer,Hashtable,Collections.synchronized(xxx)
*Vector是线程安全的,ArrayList是线程不安全的
*StringBuffer是线程安全的,StringBuilder是线程不安全的
Hashtable是线程安全的,HashMap是线程不安全的
Collections.synchronized(xxx)可将不安全的转换成安全的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值