美文网首页
多线程入门

多线程入门

作者: 某某宝 | 来源:发表于2019-05-13 08:33 被阅读0次

一、多线程

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.png

3.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; 
} 
}

相关文章

网友评论

      本文标题:多线程入门

      本文链接:https://www.haomeiwen.com/subject/vmbhaqtx.html