----------------------
ASP.Net+Android+IOS开发、
.Net培训、期待与您交流! ----------------------进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:进程中一个独立的控制单元,实质上是线程在控制着进程的执行,一个进程中至少有一个线程
JVM Java Virtual Machine 启动的时候会有一个进程java.exe
该进程中至少有一个线程负责java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程
扩展:其实详细分析JVM运行情况发现,JVM启动不止一个线程,至少还有一个负责垃圾回收机制的线程
创建线程的第一种方式:继承Thread类,并覆盖其run()方法
步骤:
1.定义类继承Thread
2.覆盖Thread类中的run(),目的是将自定义的代码存储在run方法中,让自定义线程运行
3.调用线程使用start(),start()有两个作用:启动线程,调用run方法
以上代码执行后,每一次结果都不同,原因:
多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。(在某一个时刻,只能有一个程序在运行,除非是多核CPU)
即CPU在做快速切换。
从而引出多线程的一个特性:随机性(哪个线程执行多久,CPU说了算)
为什么要覆盖run方法?
Thread类用于描述线程,该类就定义了一个方法,用于存储线程要运行的代码,该存储方法就是run方法
即Thread类中的run方法,用于存储线程要运行的代码;如主线程运行的就是main函数下的代码
示例:
创建两个线程,和主线程交替运行
获取线程默认名称的方法:getName()
线程默认名称:Thread-编号,编号从0开始
示例:
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法
3.通过Thread类建立线程对象
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法;
多线程应用:
简单的卖票程序:多窗口同时售票,共100张票
第一种方式:
第二种方式:
实现方式和继承方式有什么区别? 面试常见
实现方式的好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。
因为如果使用继承方式,那么继承Thread的就不能继承其他类了
两种方式存放位置区别:
继承Thread:线程代码存放在Thread子类的run方法中
示例:
关于sleep():
public static void sleep(long millis) throws InterruptedException
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
以上问题原因:
当多条语句在操作同一个线程共享数据时,若一个线程对多条语句只执行了一部分,还没有完全执行,另一个线程也参与进来执行时,就会导致共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在该线程执行过程中,其他线程不可以参与执行
Java对于多线程的安全问题提供了专业的解决方式:同步代码块
同步的前提:非常重要
1.必须要有两个或者两个以上的线程 (比如懒汉式单例设计模式)
2.必须是多个线程使用同一个锁
3.必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
同步代码块格式:
synchronized(对象)
{
需要被同步的代码(操作共享数据的代码,但要注意循环代码)
}
注:线程进入synchronized之后,synchronized判断对象(对象如同锁,可以引用Object对象也可以自定义新的对象)的标志位是否是开,若开,则放线程进入,进入后,synchronized把该对象的标志位改为关,然后进入的线程再执行内部(synchronized内部)代码,执行完之后线程出去了,synchronized才会把标志位改为开
示例:
通过synchronized解决问题示例:
需求:
银行有一个金库
有四个储户分别存2000元,每次存100,存20次
目的:该程序是否有安全问题,如果有,如何解决?
多线程如何找问题:
1.明确哪些代码是多线程运行代码 (上题中run()和add())
2.明确共享数据 (上题中共享数据 b,sum)
3.明确多线程运行代码中哪些语句是操作共享数据的(问题容易出现在多条语句之间)
上题的另一种解决方式:(同步函数的应用,也要注意循环体)
同步函数、同步代码块用的都是什么锁?
同步代码块:用的是参数中的对象,该对象就是它的锁
synchronized(对象)
{
代码;
}
同步函数:同步函数需要被对象调用(除静态方法),函数都有一个所属对象的引用,就是this,所以非静态同步函数使用的锁是this
验证为何同步函数的锁是this?
两个线程,一个使用同步函数,一个使用同步代码块
sum出现重复值,则说明程序存在问题
如果同步函数被静态修饰,使用的锁是该函数所在类的字节码文件对象 该同步函数所在类类名.class 该对象类型Class
注:静态方法中使用同步代码块,代码块中的对象也应该是该类所属的字节码文件对象,即方法所在类类名.class
原因:
1.静态方法中不可以定义this
2.静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象
示例:
当两个线程在类中单独存在时,同步锁就需要更加注意它的前提了
1.两个线程或以上
2.同一锁(对象)
示例:
---------------------- ASP.Net+Android+IOS开发、 .Net培训、期待与您交流! ----------------------详细请查看: http://edu.csdn.net
线程:进程中一个独立的控制单元,实质上是线程在控制着进程的执行,一个进程中至少有一个线程
JVM Java Virtual Machine 启动的时候会有一个进程java.exe
该进程中至少有一个线程负责java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程
扩展:其实详细分析JVM运行情况发现,JVM启动不止一个线程,至少还有一个负责垃圾回收机制的线程
创建线程的第一种方式:继承Thread类,并覆盖其run()方法
步骤:
1.定义类继承Thread
2.覆盖Thread类中的run(),目的是将自定义的代码存储在run方法中,让自定义线程运行
3.调用线程使用start(),start()有两个作用:启动线程,调用run方法
class Demo extends Thread
{
public void run()
{
for (int x=0; x<60; x++)
System.out.println("Demo Run---"+x); //由d线程执行
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo(); //创建好一个线程
d.start(); //启动线程,调用run()。若直接用d.run();则不会启动自定义线程
for (int x=0; x<60; x++)
System.out.println("ThreadDemo Run---"+x); //由main函数线程执行
}
}
以上代码执行后,每一次结果都不同,原因:
多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。(在某一个时刻,只能有一个程序在运行,除非是多核CPU)
即CPU在做快速切换。
从而引出多线程的一个特性:随机性(哪个线程执行多久,CPU说了算)
为什么要覆盖run方法?
Thread类用于描述线程,该类就定义了一个方法,用于存储线程要运行的代码,该存储方法就是run方法
即Thread类中的run方法,用于存储线程要运行的代码;如主线程运行的就是main函数下的代码
示例:
创建两个线程,和主线程交替运行
class Demo extends Thread
{
private String name;
Demo(){}
Demo(String name)
{
this.name = name;
}
public void run()
{
for (int x=0; x<100; x++)
System.out.println(name+"---"+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo("第1个自定义线程");
Demo d2 = new Demo("第2个自定义线程"); //创建第二个线程
d.start(); //线程1启动并执行run()
d2.start(); //线程2启动并执行run()
for (int x=0; x<100; x++)
System.out.println("ThreadDemo Run---"+x);
}
}
获取线程默认名称的方法:getName()
线程默认名称:Thread-编号,编号从0开始
示例:
class Demo extends Thread
{
Demo(){}
Demo(String name)
{
super(name); //Thread中有构造函数 Thread(String name),所以这里直接拿过来用
}
public void run()
{
for (int x=0; x<100; x++)
System.out.println(this.getName()+"-"+x);
/*
* this.getName()==Thread.currentThread().getName(),
* 注意:this的对象为继承Thread的情况下可以使用this关键字,
* 否则只能使用Thread.currentThread().getName(),通过Runnable接口
* 实现多线程方式中就需通过这种方式
*/
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo("第1个自定义线程");
Demo d2 = new Demo("第2个自定义线程"); //创建第二个线程
d.start(); //线程1启动并执行run()
d2.start(); //线程2启动并执行run()
for (int x=0; x<100; x++)
System.out.println("ThreadDemo Run---"+x);
}
}
static Thread currentThread() 获取当前线程对象
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法
3.通过Thread类建立线程对象
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法;
多线程应用:
简单的卖票程序:多窗口同时售票,共100张票
第一种方式:
class Some extends Thread
{
private static int tick = 100; //static关键字:各线程共享的数据。若不加static,总共就会卖出400张票
public void run()
{
while (tick > 0)
{
System.out.println(currentThread().getName()+"..."+tick--);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Some s1 = new Some();
Some s2 = new Some();
Some s3 = new Some();
Some s4 = new Some();
s1.start();
s2.start();
s3.start();
s4.start();
}
}
第二种方式:
class Some implements Runnable
{
/*
*这里的成员变量tick不用加static修饰符是因为再ThreadDemo类中,各个线程同时使用的是一个对象s
*而上例中,每个线程使用的是不同的Some对象s1-s4
*/
private int tick = 100;
public void run()
{
while (tick > 0)
{
System.out.println(Thread.currentThread().getName()+"..."+tick--);
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Some s = new Some();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(s);
Thread t4 = new Thread(s);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
实现方式和继承方式有什么区别? 面试常见
实现方式的好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。
因为如果使用继承方式,那么继承Thread的就不能继承其他类了
两种方式存放位置区别:
继承Thread:线程代码存放在Thread子类的run方法中
实现Runnable:线程代码存放在接口的子类的run方法中
示例:
class Some implements Runnable
{
private int tick = 100;
public void run()
{
while (tick > 0)
{ //当tick=1时,而0线程进来之后被CPU切换出去,1线程就过了tick>0的判断也会进来,后面的线程也可能会
try{Thread.sleep(10);}catch(Exception e){} //用sleep导致的冻结状态演示CPU切换导致线程进入阻塞状态,从而引发安全问题
System.out.println(Thread.currentThread().getName()+"..."+tick--); //问题:tick打印出0,-1,-2等不符合正常情况的值
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Some s = new Some();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(s);
Thread t4 = new Thread(s);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
关于sleep():
public static void sleep(long millis) throws InterruptedException
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
以上问题原因:
当多条语句在操作同一个线程共享数据时,若一个线程对多条语句只执行了一部分,还没有完全执行,另一个线程也参与进来执行时,就会导致共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在该线程执行过程中,其他线程不可以参与执行
Java对于多线程的安全问题提供了专业的解决方式:同步代码块
同步的前提:非常重要
1.必须要有两个或者两个以上的线程 (比如懒汉式单例设计模式)
2.必须是多个线程使用同一个锁
3.必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
同步代码块格式:
synchronized(对象)
{
需要被同步的代码(操作共享数据的代码,但要注意循环代码)
}
注:线程进入synchronized之后,synchronized判断对象(对象如同锁,可以引用Object对象也可以自定义新的对象)的标志位是否是开,若开,则放线程进入,进入后,synchronized把该对象的标志位改为关,然后进入的线程再执行内部(synchronized内部)代码,执行完之后线程出去了,synchronized才会把标志位改为开
示例:
class Some implements Runnable
{
private int tick = 1000;
Object obj = new Object;
public void run()
{
/*
若像下面这样写,也是不合理的,因为同步锁住了一个循环,只有一个进程进去把循环结束了才能让另外的进程进去(循环结束代表票卖完,这样其他线程就一点用处都没有了,失去了多线程的意义(成了单线程))
synchronized(obj) //也可以新建一个其他对象
{
while (tick > 0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..."+tick--);
}
}
*/
while (tick>0)
{
synchronized(obj)
{
if (tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..."+tick--);
}
}
}
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Some s = new Some();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(s);
Thread t4 = new Thread(s);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
通过synchronized解决问题示例:
需求:
银行有一个金库
有四个储户分别存2000元,每次存100,存20次
目的:该程序是否有安全问题,如果有,如何解决?
class Bank
{
private int sum;
Object obj = new Object();
public void add(int n)
{
/*
sum = sum + n;
//问题可能出现的地方 当t1进来sum=100时,CPU切出去了,没有执行下面的打印语句,t2进来之后,sum=200,这时候t1恢复并执行答应语句时,本该打印sum=100的,却打印sum=200了
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("sum="+sum);
*/
//解决办法如下
synchronized(obj)
{
sum = sum + n;
try{Thread.sleep(0);}catch(Exception e){} //为了测试问题时候能解决而故意加入的可能引起问题代码
System.out.println(Thread.currentThread().getName()+"---"+"sum="+sum);
}
}
}
class Customer implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x=0; x<20; x++) //储户存20次的动作
{
b.add(100); //每次存100元
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Customer cus = new Customer();
Thread t1 = new Thread(cus);
Thread t2 = new Thread(cus);
Thread t3 = new Thread(cus);
Thread t4 = new Thread(cus);
t1.start(); //第一个储户存钱
t2.start(); //第二个储户存钱
t3.start(); //第三个储户存钱
t4.start(); //第四个储户存钱
}
}
多线程如何找问题:
1.明确哪些代码是多线程运行代码 (上题中run()和add())
2.明确共享数据 (上题中共享数据 b,sum)
3.明确多线程运行代码中哪些语句是操作共享数据的(问题容易出现在多条语句之间)
上题的另一种解决方式:(同步函数的应用,也要注意循环体)
class Bank
{
private int sum;
Object obj = new Object();
//解决办法如下
public synchronized void add(int n) //通过synchronized修饰函数,让函数有同步的功能
{
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){} //为了测试问题时候能解决而故意加入的可能引起问题代码
System.out.println(Thread.currentThread().getName()+"---"+"sum="+sum);
/*
synchronized(obj) //同步代码块或者同步函数,选其一即可,但为了资源合理利用,需考虑用哪个更有效率
{
sum = sum + n;
try{Thread.sleep(0);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---"+"sum="+sum);
}
*/
}
}
class Customer implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x=0; x<20; x++) //储户存20次的动作
{
b.add(100); //每次存100元
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Customer cus = new Customer();
Thread t1 = new Thread(cus);
Thread t2 = new Thread(cus);
Thread t3 = new Thread(cus);
Thread t4 = new Thread(cus);
t1.start(); //第一个储户存钱
t2.start(); //第二个储户存钱
t3.start(); //第三个储户存钱
t4.start(); //第四个储户存钱
}
}
同步函数、同步代码块用的都是什么锁?
同步代码块:用的是参数中的对象,该对象就是它的锁
synchronized(对象)
{
代码;
}
同步函数:同步函数需要被对象调用(除静态方法),函数都有一个所属对象的引用,就是this,所以非静态同步函数使用的锁是this
验证为何同步函数的锁是this?
两个线程,一个使用同步函数,一个使用同步代码块
sum出现重复值,则说明程序存在问题
class Bank
{
private int sum;
private boolean flag = true;
public void setFlag(boolean flag)
{
this.flag = flag;
}
Object obj = new Object();
public void add(int n)
{
if(flag)
{
synchronized(obj) //obj对象的时候,输出结果sum会出现重复值。把obj改成this就不会出现重复值
{
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){} //若不用synchronized,容易出现问题的地方
System.out.println(Thread.currentThread().getName()+"---"+"sum="+sum);
}
}
else
lock(n);
}
public synchronized void lock(int n) //同步函数
{
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){} //通过此语句构造问题的存在
System.out.println(Thread.currentThread().getName()+"---lock---"+"sum="+sum);
}
}
class Customer implements Runnable
{
private Bank b = new Bank();
public void flaseFlag()
{
b.setFlag(false);
}
public void run()
{
for(int x=0; x<10; x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Customer cus = new Customer();
Thread t1 = new Thread(cus);
Thread t2 = new Thread(cus);
t1.start(); //第一个储户存钱
try{Thread.sleep(10);}catch(Exception e){} //为了避免主线程早于t1执行,若早于t1,则会出现全走同步函数路线,导致实验无法证明结果(原因,t1还没判断flag的时候,主线程却已经执行过cus.flaseFlag()导致flag变成false,等t1再判断的时候flag因是false而选择lock()路线)
cus.flaseFlag();
t2.start(); //第二个储户存钱
}
}
如果同步函数被静态修饰,使用的锁是该函数所在类的字节码文件对象 该同步函数所在类类名.class 该对象类型Class
注:静态方法中使用同步代码块,代码块中的对象也应该是该类所属的字节码文件对象,即方法所在类类名.class
原因:
1.静态方法中不可以定义this
2.静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象
示例:
class Bank
{
private static int sum;
private boolean flag = true;
public void setFlag(boolean flag)
{
this.flag = flag;
}
Object obj = new Object();
public void add(int n)
{
if(flag)
{
synchronized(Bank.class) //该同步函数所在类类名.class 该对象类型Class
{
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---"+"sum="+sum);
}
}
else
lock(n);
}
public static synchronized void lock(int n) //静态同步函数
{
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"---lock---"+"sum="+sum);
}
}
class Customer implements Runnable
{
private Bank b = new Bank();
public void flaseFlag()
{
b.setFlag(false);
}
public void run()
{
for(int x=0; x<10; x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Customer cus = new Customer();
Thread t1 = new Thread(cus);
Thread t2 = new Thread(cus);
t1.start(); //第一个储户存钱
try{Thread.sleep(10);}catch(Exception e){}
cus.flaseFlag();
t2.start(); //第二个储户存钱
}
}
当两个线程在类中单独存在时,同步锁就需要更加注意它的前提了
1.两个线程或以上
2.同一锁(对象)
示例:
class Res
{
String name;
String sex;
}
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r) //在Input类中其实只有t1线程,要使用synchronized,必须两个或两个以上线程
{
if(x==0)
{
r.name = "mike";
r.sex = "man";
}
else
{
r.name = "小红";
r.sex = "女";
}
}
x = (x+1)%2; //为了if和else都能执行到
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
synchronized(r) //t2线程操作的也是Res资源,和t1操作的是同一资源,所以这里也要同步,这样就构成了两个线程。里面的锁r是内存中单独存在的对象new Res(),都已在Input和Output构造函数中导入
{
System.out.println(r.name+"...."+r.sex);
}
}
}
class InputOutputDemo
{
public static void main(String[] args)
{
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
---------------------- ASP.Net+Android+IOS开发、 .Net培训、期待与您交流! ----------------------详细请查看: http://edu.csdn.net