一、多线程
1、概念:
多任务同时执行就是多线程,如果没有任务,就不需要使用多线程
线程和进程之间的区别:
进程:资源分配的最小单位
线程:cpu调度的最小单位
一个进程可以包含1~n个线程
2、线程开启的方式:(重要)
2.1 run()方法:
多线程的入口
定义代码|调用其他方法都可以
调用start()方法,开启多线程后,Java 虚拟机会调用该线程的 run 方法。
2.2 三种开启线程的方式:
- 继承Thread类,重写run()方法
创建子类对象,调用start()方法,开启多线程
public class ThreadDemo01 extends Thread {
public static void main(String[] args) {
//线程开启方法
//2.创建子类对象,调用start()方法,开启多线程
//void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
ThreadDemo01 td=new ThreadDemo01();
td.start();
//或 Thread(Runnable target, String name) 分配新的 Thread 对象。
new Thread(new ThreadDemo01(),"线程1").start();
} /
/1.继承Thread类,重写run方法
@Override public void run() {
System.out.println(Thread.currentThread().getName()+"重写");
}
}
- 实现Runnable接口,重写run()方法 ***
开启线程: Thread类做代理,调用Thread类中的start方法开启线程
优点:
避免了单继承的局限性
实现资源共享
class ThreadDemo implements Runnable{
@Override public void run() {
System.out.println("实现Runnable接口");
}
}
开启线程:
//Thread类做代理,调用Thread类中的start方法开启线程
new Thread(new ThreadDemo(),"线程2").start();
- 实现Callable接口,重写call()方法 (了解)
优点:可以有返回值,可以抛出异常
缺点:使用复杂
2.3 注意:
实现类也可以为内部类
public class InnerDemo04 {
//静态内部类
static class Inner1 implements Runnable {
@Override public void run() {
System.out.println("静态内部类");
}
}
//成员内部类
class Inner2 implements Runnable{
@Override public void run() {
System.out.println("成员内部类");
}
}
public static void main(String[] args) {
new Thread(new Inner1(),"Inner1").start();
new Thread(new InnerDemo04().new Inner2(),"Inner2").start();
//局部内部类
class Inner3 implements Runnable{
@Override public void run() {
System.out.println("局部内部类");
}
}
new Thread(new Inner3(),"Inner3").start();
//匿名内部类
new Thread(new Runnable() {
@Override public void run() {
System.out.println("匿名内部类");
}
},"匿名内部类").start();
//lambda表达式 new Thread(()->{ System.out.println("lambda");} ,"Lambda").start(); } }
3、线程状态
3.1 五种状态
新生状态:new线程对象的时候,这个线程处于新生状态
就绪状态:调用start()方法,线程进入就绪状态,进入到就绪队列,进入就绪状态代表线程有能力执行,但是要等到cpu调用,分配时间片才能执行
运行状态:当前cpu调度,分配时间片给就绪状态的线程,当前线程执行
阻塞状态:sleep...
终止状态:线程结束
clipboard.png3.2、sleep()方法:延迟
放大问题的可能性
模拟网络延迟
public class SleepDemo05 implements Runnable{
public static void main(String[] args) {
new Thread(new SleepDemo05()).start();
}
@Override public void run() {
for (int i = 10; i!=0; i--) {
try {
//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
//此操作受到系统计时器和调度程序精度和准确性的影响。
Thread.sleep(1000);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
System.out.println("倒计时"+i+"秒");
}
System.out.println("游戏结束");
}
}
3.3、yield()方法:礼让
如果当前线程调用yield方法,会直接进入到就绪状态
public class YieldDemo06 implements Runnable {
public static void main(String[] args) {
new Thread(new YieldDemo06(),"1").start();
new Thread(new YieldDemo06(),"2").start();
}
@Override public void run() {
System.out.println(Thread.currentThread().getName()+"开始啦");
Thread.yield();
//礼让线程
System.out.println(Thread.currentThread().getName()+"结束啦");
}
}
运行结果(进入就绪状态后,最终还是要看cpu的分配,并非按照顺序一定先执行哪一个线程)
2开始啦
1开始啦
2结束啦
1结束啦
3.4、join()方法:插队
public class JoinDemo07 implements Runnable{
public static void main(String[] args) {
new Thread(new JoinDemo07(),"线程1").start();
}
@Override public void run() {
System.out.println(Thread.currentThread().getName()+"启动");
Thread t=new Thread(new JumpAQueue(),"线程2");
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束");
}
}
class JumpAQueue implements Runnable{
@Override public void run() {
System.out.println(Thread.currentThread().getName()+"启动"); System.out.println(Thread.currentThread().getName()+"结束");
}
}
运行结果
线程1启动 线程2启动 线程2结束 线程1结束
3.5、总结
- 如何控制线程的终止:
调用stop(),destory(),已过时,不推荐
线程正常执行结束
添加标识控制
- 进入线程就绪状态的几种情况:
start()方法
yield 礼让线程
线程之前切换
解除阻塞状态,线程进入到就绪状态
- 进入线程阻塞状态的几种情况:
sleep方法
join方法
wait方法
IO操作
4、控制线程安全:加锁 使用关键字synchronized (
针对问题:多个线程同时操作同一份资源的时候,可能会发生线程不安全问题
- 同步方法: 在方法上面加锁
同步静态方法
同步成员方法
/* *
模拟案例:12306,3个人同时买100张票 * 方法上加锁,控制线程安全
*/
public class MethodSynchronized08 implements Runnable {
// 100张票
int tickets = 100;
boolean flag=true;
@Override public void run() {
while (flag){ flag=syn();
}
}
public synchronized boolean syn() {
if(tickets<=0) return false;
// 模拟购票延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 买票
System.out.println(Thread.currentThread().getName() + "买了第" + tickets-- + "张票"); return true;
}
public static void main(String[] args) {
//票为共享资源,因此需为同一对象
MethodSynchronized08 ms=new MethodSynchronized08();
//开启线程
new Thread(ms, "1号").start();
new Thread(ms, "2号").start();
new Thread(ms, "3号").start();
}
}
- 同步块: synchronized(){}
锁this,锁资源,锁类
/* *
锁 块 控制线程安全 * 锁this就是锁对象,所对象会锁住这个对象中的所有成员(资源),如果只想锁住其中的某个资源,可以只锁这个资源
*/
public class Synchronized09 implements Runnable {
// 100张票
int tickets = 100;
@Override public void run() {
while (true) {
synchronized (this) {
if (tickets >= 1) {
// 模拟购票延时 try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 买票
System.out.println(Thread.currentThread().getName() + "买了第" + tickets-- + "张票");
}
}
}
}
public static void main(String[] args) {
// 票为共享资源,因此需为同一对象
Synchronized09 ms = new Synchronized09();
// 开启线程
new Thread(ms, "1号").start(); ;
new Thread(ms, "2号").start(); ;
new Thread(ms, "3号").start(); ;
}
}
锁资源 一般就是指成员属性,锁一定要锁不变的内容,对象的地址永远不变,自定义的引用数据类型肯定能锁住
public class Synchronized10 implements Runnable {
// 票
Tickets tickets=new Tickets();
@Override public void run() {
while (true) {
//锁资源的地址,是对象肯定能锁住 synchronized (tickets) {
if (tickets.num >= 1) { // 模拟购票延时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace();
}
//
买票
System.out.println(Thread.currentThread().getName() + "买了第" + tickets.num-- + "张票");
}
}
}
}
public static void main(String[] args) {
// 票为共享资源,因此需为同一对象
Synchronized09 ms = new Synchronized09();
// 开启线程
new Thread(ms, "1号").start();
new Thread(ms, "2号").start();
new Thread(ms, "3号").start();
}
}
class Tickets { int num = 100;
}
锁类的class对象,锁类,锁不变的东西
public class Synchronized11 implements Runnable {
// 100张票
int tickets = 100;
boolean flag = true;
@Override public void run() {
while (true) {
synchronized (Synchronized11.class) {
if (tickets >= 1) {
// 模拟购票延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} //
买票
System.out.println(Thread.currentThread().getName() + "买了第" + tickets-- + "张票");
}
}
}
}
public static void main(String[] args) {
// 票为共享资源,因此需为同一对象
Synchronized11 ms = new Synchronized11();
// 开启线程
new Thread(ms, "1号").start();
new Thread(ms, "2号").start();
new Thread(ms, "3号").start();
}
}
二、总结
1、 注意:
锁类,这个类的所有对象都被锁住了
锁this,值只锁住当前对象
锁的必须是不变的内容
锁的范围太大,效率低,锁的范围太小,容易锁不住
2、单例:懒汉式是线程不安全的
锁静态方法:
锁类锁静态方法都是锁类,在static方法中不能使用this,所以不能锁对象
double check 双重检查,效率更高 :
public class Demo01 {
public static void main(String[] args) {
new Thread(()->{
System.out.println(SingleTon.newInstance()); }).start();
new Thread(()->{
System.out.println(SingleTon.newInstance()); }).start(); }
}
class SingleTon{
//1.私有的静态的该类的应用
private static SingleTon single=null;
//2.私有的构造器 private SingleTon(){}
//3.公共的静态的访问方式
/*public synchronized static SingleTon newInstance(){ if(single==null){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } single=new SingleTon(); } return single; }*/
//双重检查双重锁
public static SingleTon newInstance(){
if(single==null){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// synchronized (SingleTon.class) {
if(single==null){
single=new SingleTon();
}
}
}
return single;
}
}
网友评论