java多线程及安全问题
2012-12-06 11:18
302 查看
进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
通过对api的查找,java已经提供了对线程这类事物的描述,就是Thread类
1.创建线程的第一种方式:继承Thread类。
步骤:
1.定义类继承Thread类
2.复写Thread类中的run方法。
目的:将自定义代码存储在run方法,让线程运行。
3.调用线程的start方法,启动线程,调用run方法。
class Demo extends Thread
{
//run()方法为线程的主要方法,所要依赖线程运行的代码都要在这个函数里运行
public void run()
{
//多线程运行的代码块
}
}
main(String args[])
{
//创建Thread子类对象的同时,线程也被创建了。
Demo d = new Demo();
//用start方法开启线程,执行Demo类中的run方法。
d.start();
//如果调用run方法,就仅仅是调用了Demo类中run方法,并未启动新的线程
d.run();
}
各种方法的作用:
sleep(time) 使线程冻结一段时间,时间到后线程自动运行;
wait() 使线程暂停,等待notify() 方法唤醒后线程方可继续运行;
stop() 结束当前线程。
例子:多个窗口同时卖票。
利用第一种创建线程的方式实现卖票
class Ticket extends Thread{
//利用静态成员变量共享票数。但是静态变量比较耗费资源。
private static int tick = 100;
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+": "+tick--);
}
}
}
}
public static void main(String args[]){
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}
2.创建线程的第二种方式:实现Runable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run 方法中。
3.通过Thread类建立线程对象。
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象时Runnable节后的子类对象。
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
售票例子:
class Ticket implements Runnable{
private int tick = 100;
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+": "+tick--);
}
}
}
}
public static void main(String args[]){
//与上面的售票相比,实现票数共享是通过只创建一个Runnable子类对象来实现
//将此对象通过多个线程来运行,以达到多线程效果。
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
new Thread(t4).start();
}
实现方式和继承方式有什么区别呢?(面试常考)
现方式好处:避免了单继承的局限性。
在定义线程时,建议使用实现方式。
java机制只允许继承一个父类,但是可以实现多个接口,有的时候一个子类需要继承父类
但同时又需要被多线程执行时,就不可能再继续用继承Thread类的方式实现多线程了,
只能通过实现Runnable 接口来实现多线程。
多线程安全问题:
问题的原因:
当多条哦语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完。
另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可参与执行。
同步代码块。
synchronized(对象){
//需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu执行权,也进不去,因为没有获取锁。
同步的前提:
1.必须要有两个或者两个以上的线程。
2.必须是多个线程使用同一个锁
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
如何找问题:
1.明确哪些代码是多线程运行代码。
2.明确共享数据。
3.明确多线程运行代码中哪些语句是操作共享数据的。
同步有两种形式:
1.同步代码块 : synchronized(对象){
//需要被同步的代码
}
2.同步函数 : 把synchronized作为修饰符放在函数上,此函数就具备了同步的特性。
同步函数用得到是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
所以同步函数使用的锁是this。利用如下代码验证:
private int tick = 100;
public void run(){
if(flag){
while(true){
synchronized(this/*此处未为验证精髓*/){
//同步代码块
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..code.."+tick--)
}
}
}
}else
while(true)
show();
}
public synchronized void show(){
//需要同步的内容
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..show.."+tick--)
}
}
//通过观察输出票数是否安全便可知道,同步函数的锁是否是this
public void main(String args[]){
new Thread(对象).run();
//让线程等待10ms,以便确认0线程已进入代码块
try{Thread.sleep(10);}catch(Exception e){}
flag=false;
new Thread(对象).run();
}
静态同步函数,使用的锁是什么呢?
通过验证,不是this,因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是Class ,所使用的锁是该方法所在类的字节码文件对象。
(重点)单例设计模式:
饿汉式:
class Single{
//先创建对象,需要的时候直接调用
private static final Single s = new Single();
//将构造函数私有化,禁止外界的访问。
private Single(){}
public static Single getInstance(){
return s;
}
}
懒汉式:
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
//双重判断,提高懒汉式的效率,一次创建对象以后,就不再需要判断锁
if(s==null){
//多线程多次判断锁,影响效率
synchronized(Single.class){
//如果不存在对象,才要创建对象,称延迟加载
if(s==null)
s=new Single();
}
}
return s;
}
}
死锁:例子:
public void run(){
if(flag){
while(true){
synchronized(MyLock.locka){
Syste.out.printlnt("if locka");
synchronized(MyLock.lockb){
Syste.out.printlnt("if lockb");
}
}
}
}else{
while(true){
synchronized(MyLock.lockb){
Syste.out.printlnt("else lockb");
synchronized(MyLock.locka){
Syste.out.printlnt("else locka");
}
}
}
}
}
class MyLock {
public static Object locka=new Object();
public static Object lockb=new Object();
}
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
通过对api的查找,java已经提供了对线程这类事物的描述,就是Thread类
1.创建线程的第一种方式:继承Thread类。
步骤:
1.定义类继承Thread类
2.复写Thread类中的run方法。
目的:将自定义代码存储在run方法,让线程运行。
3.调用线程的start方法,启动线程,调用run方法。
class Demo extends Thread
{
//run()方法为线程的主要方法,所要依赖线程运行的代码都要在这个函数里运行
public void run()
{
//多线程运行的代码块
}
}
main(String args[])
{
//创建Thread子类对象的同时,线程也被创建了。
Demo d = new Demo();
//用start方法开启线程,执行Demo类中的run方法。
d.start();
//如果调用run方法,就仅仅是调用了Demo类中run方法,并未启动新的线程
d.run();
}
各种方法的作用:
sleep(time) 使线程冻结一段时间,时间到后线程自动运行;
wait() 使线程暂停,等待notify() 方法唤醒后线程方可继续运行;
stop() 结束当前线程。
例子:多个窗口同时卖票。
利用第一种创建线程的方式实现卖票
class Ticket extends Thread{
//利用静态成员变量共享票数。但是静态变量比较耗费资源。
private static int tick = 100;
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+": "+tick--);
}
}
}
}
public static void main(String args[]){
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}
2.创建线程的第二种方式:实现Runable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run 方法中。
3.通过Thread类建立线程对象。
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象时Runnable节后的子类对象。
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
售票例子:
class Ticket implements Runnable{
private int tick = 100;
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+": "+tick--);
}
}
}
}
public static void main(String args[]){
//与上面的售票相比,实现票数共享是通过只创建一个Runnable子类对象来实现
//将此对象通过多个线程来运行,以达到多线程效果。
Ticket t1 = new Ticket();
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
new Thread(t4).start();
}
实现方式和继承方式有什么区别呢?(面试常考)
现方式好处:避免了单继承的局限性。
在定义线程时,建议使用实现方式。
java机制只允许继承一个父类,但是可以实现多个接口,有的时候一个子类需要继承父类
但同时又需要被多线程执行时,就不可能再继续用继承Thread类的方式实现多线程了,
只能通过实现Runnable 接口来实现多线程。
多线程安全问题:
问题的原因:
当多条哦语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完。
另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可参与执行。
同步代码块。
synchronized(对象){
//需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu执行权,也进不去,因为没有获取锁。
同步的前提:
1.必须要有两个或者两个以上的线程。
2.必须是多个线程使用同一个锁
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
如何找问题:
1.明确哪些代码是多线程运行代码。
2.明确共享数据。
3.明确多线程运行代码中哪些语句是操作共享数据的。
同步有两种形式:
1.同步代码块 : synchronized(对象){
//需要被同步的代码
}
2.同步函数 : 把synchronized作为修饰符放在函数上,此函数就具备了同步的特性。
同步函数用得到是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
所以同步函数使用的锁是this。利用如下代码验证:
private int tick = 100;
public void run(){
if(flag){
while(true){
synchronized(this/*此处未为验证精髓*/){
//同步代码块
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..code.."+tick--)
}
}
}
}else
while(true)
show();
}
public synchronized void show(){
//需要同步的内容
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"..show.."+tick--)
}
}
//通过观察输出票数是否安全便可知道,同步函数的锁是否是this
public void main(String args[]){
new Thread(对象).run();
//让线程等待10ms,以便确认0线程已进入代码块
try{Thread.sleep(10);}catch(Exception e){}
flag=false;
new Thread(对象).run();
}
静态同步函数,使用的锁是什么呢?
通过验证,不是this,因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class 该对象的类型是Class ,所使用的锁是该方法所在类的字节码文件对象。
(重点)单例设计模式:
饿汉式:
class Single{
//先创建对象,需要的时候直接调用
private static final Single s = new Single();
//将构造函数私有化,禁止外界的访问。
private Single(){}
public static Single getInstance(){
return s;
}
}
懒汉式:
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
//双重判断,提高懒汉式的效率,一次创建对象以后,就不再需要判断锁
if(s==null){
//多线程多次判断锁,影响效率
synchronized(Single.class){
//如果不存在对象,才要创建对象,称延迟加载
if(s==null)
s=new Single();
}
}
return s;
}
}
死锁:例子:
public void run(){
if(flag){
while(true){
synchronized(MyLock.locka){
Syste.out.printlnt("if locka");
synchronized(MyLock.lockb){
Syste.out.printlnt("if lockb");
}
}
}
}else{
while(true){
synchronized(MyLock.lockb){
Syste.out.printlnt("else lockb");
synchronized(MyLock.locka){
Syste.out.printlnt("else locka");
}
}
}
}
}
class MyLock {
public static Object locka=new Object();
public static Object lockb=new Object();
}
相关文章推荐
- java基础—多线程下的单例设计模式的安全问题
- java基础知识回顾之java Thread类学习(七)--java多线程安全问题(死锁)
- Java多线程安全问题及解决方案
- Java多线程技术初识——4. 多线程安全问题的解决
- (3)Java多线程之安全问题-上
- 4000 java中多线程的安全问题以及解决办法(2)
- java 多线程安全问题-同步代码块
- Java——多线程存在的安全问题
- Java——多线程安全问题
- java基础知识回顾之java Thread类学习(四)--java多线程安全问题(锁)
- Java学习笔记45(多线程二:安全问题以及解决原理)
- Java基础——多线程的安全问题
- Java多线程中线程安全与锁问题
- JAVA多线程不安全问题解决方案(多线程并发同一资源)。
- java-多线程安全问题
- Java——多线程安全问题
- (4)Java多线程之安全问题-下
- java多线程安全问题的解决
- Java多线程安全问题
- java中多线程的安全问题以及解决办法