1、概述
(看最后一个图)
进程包含线程;线程是进程的一部分,是轻量级的进程,是不同的执行路径。
多线程:一条路径不能满足要求,另开辟一条路径。
真正的多线程是有多个cpu,有多核。cpu调度哪个程序哪段代码有个调度,调度过程中有个时间片(纳秒级别)。把线程a挂起,再执行线程b,时间片非常的短,看起来像是a和b同时在执行。
什么时候需要执行多线程?
main方法(主线程)执行的时候,还有后台默默执行的gc,这个就是多线程;
异常机制;
2、java实现多线程
就是一些类和接口
模拟多线程:
/**
* 模拟龟兔赛跑
1、创建多线程 继承 Thread +重写run(线程体)
2、使用线程: 创建子类对象 + 对象.start() ,这表示线程启动,线程启动不表示线程运行
(调用start()方法,加到线程组里面去了。 它内部由cpu去管控它,cpu去调用它。
而且不要去调用线程类的run方法,那不叫启动线程,而是方法调用。)
总之:想开辟一条新路径,必须调用start()方法,加到线程组里面,由cpu自己管控。
*
* @author Administrator
*
*/
public class Rabbit extends Thread {
@Override
public void run() {
//线程体
for(int i=0;i<100;i++){
System.out.println("兔子跑了"+i+"步");
}
}
}
class Tortoise extends Thread {
@Override
public void run() {
//线程体
for(int i=0;i<100;i++){
System.out.println("乌龟跑了"+i+"步");
}
}
}
public class RabbitApp {
/**
* @param args
*/
public static void main(String[] args) {
//创建子类对象
Rabbit rab = new Rabbit();
Tortoise tor =new Tortoise();
//调用start 方法
rab.start(); //不要调用run方法,这个run方法是内部自己调用
//rab.run();
tor.start();
//tor.run();
//这里一共是5条路,加上main,后台的gc和异常。
for(int i=0;i<1000;i++){
System.out.println("main==>"+i);
}
}
}
这种方式实现了静态代理模式。run方法不能对外声明异常
package com.bjsxt.thread.create;
/**
* 静态代理 设计模式
* 1、真实角色
* 2、代理角色: 持有真实角色的引用 (婚介忙前忙后,但结婚的人还是你)
* 3、二者 实现相同的接口 (你租房子找中介公司、你结婚找婚介)
* 中介公司帮你找房子,代理角色帮真实角色实现相同的接口
* @author Administrator
*
*/
public class StaticProxy {
/**
* @param args
*/
public static void main(String[] args) {
//创建真实角色,这里没有新增方法,就可以用接口
Marry you =new You();
//创建代理角色 + 真实角色的引用(这里可以用set和get方法,也可以用构造器)
WeddingCompany company =new WeddingCompany(you);
//执行任务
company.marry();
}
}
//接口
interface Marry{
//void marry();可以省略
public abstract void marry();
}
//真实角色
class You implements Marry{
@Override
public void marry() {
System.out.println("you and 嫦娥结婚了....");
}
}
//代理角色
class WeddingCompany implements Marry{
private Marry you;
public WeddingCompany() {
}
public WeddingCompany(Marry you) {
this.you = you;
}
//注意是私有的
private void before(){
System.out.println("布置猪窝....");
}
private void after(){
System.out.println("闹玉兔....");
}
@Override
public void marry() {
before();
you.marry();
after();
}
}
Thread类实现了Runnable接口,Thread类就是代理角色,我们创建一个真实角色就可以了。
真实角色实现Runnable接口+Thread(Runnable target)代理持有真实角色的引用 = 符合代理模式
源码中:run()方法,如果target!=null,就执行target的run()方法。
代理可以有多个,表示资源可以共享
package com.bjsxt.thread.create;
/**
* 方便共享资源
* @author Administrator
*
*/
public class Web12306 implements Runnable {
private int num =50;
@Override
public void run() {
while(true){
if(num<=0){
break; //跳出循环
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
public static void main(String[] args) {
//真实角色
Web12306 web = new Web12306();
//代理
Thread t1 =new Thread(web,"路人甲");
Thread t2 =new Thread(web,"黄牛已");
Thread t3 =new Thread(web,"攻城师");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
创建多线程总结:
package com.bjsxt.thread.create;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 使用Callable创建线程
* @author Administrator
*
*/
public class Call {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程
ExecutorService ser=Executors.newFixedThreadPool(2);
Race tortoise = new Race("老不死",1000);
Race rabbit = new Race("小兔子",500);
//获取值
Future<Integer> result1 =ser.submit(tortoise) ;
Future<Integer> result2 =ser.submit(rabbit) ;
Thread.sleep(2000); //2秒
tortoise.setFlag(false); //停止线程体循环
rabbit.setFlag(false);
int num1 =result1.get();
int num2 =result2.get();
System.out.println("乌龟跑了-->"+num1+"步");
System.out.println("小兔子跑了-->"+num2+"步");
//停止服务
ser.shutdownNow();
}
}
class Race implements Callable<Integer>{
private String name ; //名称
private long time; //延时时间
private boolean flag =true;
private int step =0; //步
public Race() {
}
public Race(String name) {
super();
this.name = name;
}
public Race(String name,long time) {
super();
this.name = name;
this.time =time;
}
@Override
public Integer call() throws Exception {
while(flag){
Thread.sleep(time); //延时
step++;
}
return step;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
}
3、线程状态
start()之后 线程进入就绪状态,等待CPU调度
就绪,运行,阻塞状态 都是线程是 活的,其余的都是死的,new出来没有启动也是死的。
创建状态:new一下即可
就绪状态:调用start就进入就绪了
运行状态:cpu调度了
终止:线程体执(run方法)行完毕。 下面看线程怎么停止
package com.bjsxt.thread.status;
public class StopDemo01 {
/**
* @param args
*/
public static void main(String[] args) {
Study s =new Study();
new Thread(s).start();
//外部干涉
for(int i=0;i<100;i++){
if(50==i){ //外部干涉
s.stop();
}
System.out.println("main.....-->"+i);
}
}
}
class Study implements Runnable{
//1)、线程类中 定义 线程体使用的标识
private boolean flag =true;
@Override
public void run() {
//2)、线程体使用该标识
while(flag){
System.out.println("study thread....");
}
}
//3)、对外提供方法改变标识
public void stop(){
this.flag =false;
}
}
阻塞:
join:合并线程. (等合进来的运行完了。)
主线程中插一个 线程t,t.join表示,线程t合入到主线程中,等t执行完了再执行主线程,和方法调用效果一样。
package com.bjsxt.thread.status;
/**
* join:合并线程
* @author Administrator
*
*/
public class JoinDemo01 extends Thread {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
JoinDemo01 demo = new JoinDemo01();
Thread t = new Thread(demo); //新生成的线程
t.start();//就绪
//cpu调度 运行
for(int i=0;i<1000;i++){
if(50==i){
t.join(); //main阻塞... 等于50的时候,让demo执行完毕,再执行main线程
}
System.out.println("main...."+i);
}
}
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("join...."+i);
}
}
}
yield:暂停自己的线程(后面可能还会被调度),(静态方法)(写在谁的线程体里面就暂定谁) (让合进来的运行一会儿)
package com.bjsxt.thread.status;
public class YieldDemo01 extends Thread {
/**
* @param args
*/
public static void main(String[] args) {
YieldDemo01 demo = new YieldDemo01();
Thread t = new Thread(demo); //新生
t.start();//就绪
//cpu调度 运行
for(int i=0;i<1000;i++){
if(i%20==0){
//暂停本线程 main
Thread.yield();
}
System.out.println("main...."+i);
}
}
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("yield...."+i);
}
}
}
sleep:休眠,(静态方法)不释放锁。
倒计时:
package com.bjsxt.thread.status;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 倒计时
* 1、倒数10个数,一秒内打印一个
* 2、倒计时
* @author Administrator
*
*/
public class SleepDemo01 {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
Date endTime =new Date(System.currentTimeMillis()+10*1000);
long end =endTime.getTime();
while(true){
//输出
System.out.println(new SimpleDateFormat("mm:ss").format(endTime)); //format格式化为字符串,parse转换为日期
//等待一秒,顺序也可以放在下面
Thread.sleep(1000);
//构建下一秒时间
endTime =new Date(endTime.getTime()-1000);
//10秒以内 继续 否则 退出
if(end-10000>endTime.getTime()){
break;
}
}
}
public static void test1() throws InterruptedException{
int num =10;
while(true){
System.out.println(num--);
Thread.sleep(1000); //暂停
if(num<=0){
break;
}
}
}
}
网络延迟:
package com.bjsxt.thread.status;
/**
* Sleep模拟 网络延时 线程不安全的类
* @author Administrator
*
*/
public class SleepDemo02 {
/**
* @param args
*/
public static void main(String[] args) {
//真实角色
Web12306 web= new Web12306();
Web12306 web2 = new Web12306();
//代理
Thread t1 =new Thread(web,"路人甲");
Thread t2 =new Thread(web,"黄牛已");
Thread t3 =new Thread(web,"攻城师");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
class Web12306 implements Runnable {
private int num =50;
@Override
public void run() {
while(true){
if(num<=0){
break; //跳出循环
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
}
线程状态:
设置优先级
/**
* 优先级:概率,不是绝对的先后顺序
MAX_PRIORITY 10
NORM_PRIORITY 5 (默认)
MIN_PRIORITY 1
setPriority()
getPriority()
* @author Administrator
*
*/
4、线程同步:
java.util.Hashtable - 线程安全的,同步的
StringBuffer – 也是线程安全的,使用了synchronized。这个关键字就是锁,任何线程访问这个方法,先获得这个方法的锁,锁住了门一关别人就访问不了
等线程锁释放了,别的锁才能进来。线程体执行完毕,锁自然就释放了。
访问同一个资源才会有并发问题,访问不同的资源没事。
一般地线程不安全的效率比较高。
JDK的RunTime就是单例中饿汉
package com.bjsxt.thread.syn;
public class SynDemo01 {
/**
* @param args
*/
public static void main(String[] args) {
//真实角色
Web12306 web= new Web12306();
//代理
Thread t1 =new Thread(web,"路人甲");
Thread t2 =new Thread(web,"黄牛已");
Thread t3 =new Thread(web,"攻城师");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
/**
* 线程安全的类
* @author Administrator
*
*/
class Web12306 implements Runnable {
private int num =10;
private boolean flag =true;
@Override
public void run() {
while(flag){
test5();
}
}
//线程不安全 锁定范围不正确
public void test6(){
if(num<=0){
flag=false; //跳出循环
return ;
}
//a b c
synchronized(this){
try {
Thread.sleep(500); //模拟 延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
//线程不安全 锁定资源不正确
public void test5(){
//a b c
synchronized((Integer)num){
if(num<=0){
flag=false; //跳出循环
return ;
}
try {
Thread.sleep(500); //模拟 延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
//锁定范围不正确 线程不安全
//锁定范围小了
public void test4(){
// c 1
synchronized(this){
//b
if(num<=0){
flag=false; //跳出循环
return ;
}
}
// b
try {
Thread.sleep(500); //模拟 延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}//a -->1
//线程安全 锁定正确
public void test3(){
//a b c abc进来只能是依次地等待
//锁定this,this指的是对象
synchronized(this){
if(num<=0){
flag=false; //跳出循环
return ;
}
try {
Thread.sleep(500); //模拟 延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
//线程安全 锁定正确
public synchronized void test2(){
if(num<=0){
flag=false; //跳出循环
return ;
}
try {
Thread.sleep(500); //模拟 延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
//线程不安全
public void test1(){
if(num<=0){
flag=false; //跳出循环
return ;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
package com.bjsxt.thread.syn;
/**
* 单例设计模式:确保一个类只有一个对象
这个对象由类内部创建,外部只能使用这一个对象(JVM只有一个吧,其它类都是对它的引用)
GC..
* @author Administrator
*
*/
public class SynDemo02 {
/**
* @param args
*/
public static void main(String[] args) {
JvmThread thread1 = new JvmThread(100);
JvmThread thread2 = new JvmThread(500);
thread1.start();
thread2.start();
}
}
class JvmThread extends Thread{
private long time;
public JvmThread() {
}
public JvmThread(long time) {
this.time =time;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->创建:"+Jvm.getInstance(time));
}
}
/**
* 单例设计模式(的套路)
* 确保一个类只有一个对象
* 懒汉式 double checking(经典的双重检查)
* 1、构造器私有化,避免外部直接创建对象
* 2、声明一个私有的静态变量
* 3、创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
*/
class Jvm {
//声明一个私有的静态变量
private static Jvm instance =null; //懒得去创建对象
//构造器私有化,避免外部直接创建对象
private Jvm(){
}
//对getInstance3的改进
//创建一个对外的公共的静态方法 访问该变量,如果变量没有对象,创建该对象
public static Jvm getInstance(long time){
// c d e -->效率 提供 已经存在对象的访问效率(线程不需要等待)
if(null==instance){
// a b 。a创建了,b进来发现有对象了,就不必等其他线程拿到对象之后它才可以拿,b直接拿对象就行
synchronized(Jvm.class){
if(null==instance ){
try {
Thread.sleep(time); //延时 ,放大错误。才会加一个time
} catch (InterruptedException e) {
e.printStackTrace();
}
instance =new Jvm();
}
}
}//a
return instance;
}
//这里是同步块
public static Jvm getInstance3(long time){
//a b c d e -->效率不高 c 存在对象也需要等待
//任何时候要获取对象,如果对象存在的话,那也要进下面的方法,也要等待。锁定的是对象,线程b必须等线程a拿到对象之后才可以拿对象,有了一个等待的时间
//这里不能用this,因为静态方法里面没有this
synchronized(Jvm.class){
if(null==instance ){
try {
Thread.sleep(time); //延时 ,放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
instance =new Jvm();
}
return instance;
}
}
//这里是同步方法
public static synchronized Jvm getInstance2(long time){
if(null==instance ){
try {
Thread.sleep(time); //延时 ,放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
instance =new Jvm();
}
return instance;
}
//问题:单线程里面new出来的始终是一个对象,多线程的话就要加synchronized
public static Jvm getInstance1(long time){
if(null==instance ){
try {
Thread.sleep(time); //延时 ,放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
//两个线程进来,就有可能创建两个对象
instance =new Jvm();
}
return instance;
}
}
package com.bjsxt.thread.syn;
/**
* 单例创建的方式
* 1、懒汉式
* 1)、构造器私有化
* 2)、声明私有的静态属性
* 3)、对外提供访问属性的静态方法,确保该对象存在
*
* @author Administrator
*
*/
//懒汉式
public class MyJvm {
private static MyJvm instance;
private MyJvm(){
}
//多线程的难点:又要提高效率,又要注意安全
public static MyJvm getInstance (){
if(null==instance){ //提供效率
synchronized(MyJvm.class){
if(null==instance){ //安全
instance =new MyJvm();
}
}
}
return instance;
}
}
/**
* 饿汉式
1)、构造器私有化
* 2)、声明私有的静态属性,同时创建该对象
* 3)、对外提供访问属性的静态方法
* @author Administrator
*
*/
class MyJvm2 {
//这里是线程安全的,
//加载MyJvm2的时候instance就初始化
private static MyJvm2 instance =new MyJvm2();
private MyJvm2(){
}
public static MyJvm2 getInstance (){
return instance;
}
}
/**
* 类在使用的时候加载 ,延缓加载时间
* @author Administrator
*
*/
class MyJvm3 { //对MyJvm2的改进,提升效率的
//内部类
//加载MyJvm3的时候不一定会加载JVMholder,只有用到的时候(这里是调用getInstance时)加载。不调用该方法,JVMholder永远不会被加载
//可以延缓加载时间
private static class JVMholder{
private static MyJvm3 instance =new MyJvm3();
}
private MyJvm3(){
}
public static MyJvm3 getInstance (){
return JVMholder.instance;
}
}
死锁:过多的同步造成死锁
即:不给我钱我不给你货,你不给我货我就不给你钱
同一份资源,在线程A中用了,又在线程B中用了,就可能会造成死锁。互相不释放
package com.bjsxt.thread.syn;
/**
* 过多的同步方法可能造成死锁
* @author Administrator
*
*/
public class SynDemo03 {
/**
* @param args
*/
public static void main(String[] args) {
Object g =new Object();
Object m = new Object();
Test t1 =new Test(g,m);
Test2 t2 = new Test2(g,m);
Thread proxy = new Thread(t1); //多态不能调用新增方法
Thread proxy2 = new Thread(t2);
proxy.start();
proxy2.start();
}
}
class Test implements Runnable{
Object goods ;
Object money ;
public Test(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while(true){
test();
}
}
public void test(){
synchronized(goods){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(money){
}
}
System.out.println("一手给钱");
}
}
class Test2 implements Runnable{
Object goods ;
Object money ;
public Test2(Object goods, Object money) {
super();
this.goods = goods;
this.money = money;
}
@Override
public void run() {
while(true){
test();
}
}
public void test(){
synchronized(money){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(goods){
}
}
System.out.println("一手给货");
}
}
生产者消费者模式(不是设计模式,是解决多线程的问题)
如何解决死锁,看看生产者消费者模式。
先生产再消费,有东西了再消费,没有东西先生产(用的是同一份资源)
package com.bjsxt.thread.pro;
/**
一个场景,共同的资源
生产者消费者模式 信号灯法
wait() :等待,释放锁 。 sleep 不释放锁
notify()/notifyAll():唤醒
他俩必须要与 synchronized一起使用
* @author Administrator
*
*/
public class Movie {
private String pic ;
//信号灯
//flag -->T 生产生产,消费者等待 ,生产完成后通知消费
//flag -->F 消费者消费 生产者等待, 消费完成后通知生产
private boolean flag =true;
/**
* 播放
* @param pic
*/
public synchronized void play(String pic){
if(!flag){ //生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始生产
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产了:"+pic); //生产了左青龙
//生产完毕
this.pic =pic;
//通知消费
this.notify();
//生产者停下
this.flag =false;
}
public synchronized void watch(){
if(flag){ //消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始消费
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费了"+pic);
//消费完毕
//通知生产
this.notifyAll();
//消费停止
this.flag=true;
}
}
5、任务调度
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
了解
Timer()
schedule(TimerTask task, Date time)
schedule(TimerTask task, Date firstTime, long period)
自学 quartz
* @author Administrator
*
*/
public class TimeDemo01 {
/**
* @param args
*/
public static void main(String[] args) {
Timer timer =new Timer();
timer.schedule(new TimerTask(){
@Override
public void run() {
System.out.println("so easy....");
}}, new Date(System.currentTimeMillis()+1000), 200);
}
}