线程 同步

多线程

多线程的好处:多个线程之间互不影响(在不同的栈空间)

My Thread mt = new MyThread();

my.strat();

//开启多线程

 

并发与并行

并发:指两个或多个事件在同一个时间段内发生(交替执行)
并行:指两个或多个事件在同一时刻发生(同时发生)

 

Thread类

构造方法:

public Thread() :分配一个新的线程对象
public Thread(String name) :分配一个指定名字的新的线程对象
public Thread(Runnable target) :分配一个带有指定目标新的线程对象
public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字

常用方法:

public String getName() :获取当前线程名称。
public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run() :此线程要执行的任务在此处定义代码。
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

获取线程的名称:

    1.使用Thread类中的方法getName();

            String getName()  返回该线程的名称

     2.可以先获取到当前正在执行的线程,使用线程中的方法getName( )获取线程的名臣

             static Thread currentThread( ) 返回对当前正在执行的线程对象的引用

设置线程的名称:

    1.使用Thread类中的方法setName(名字)

           void setName(String name)  改变线程名称,使之与参数name相同

     2.创建一个带参数的构造方法,参数传递线程的名臣,调用父类的带参构造方法,把线程名臣传递给父类,让父类(Thread)给子线程起一个名称

            Thread(String name) 分配新的Thread对象

 

public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)

毫秒数结束之后,线程继续执行

 

创建多线程程序的第一种方式创建Thread类的子类

要实现多线程程序,就必须继承Thread类

实现步骤:

        1.传教一个Thread类的子类

        2.在Thread类的子类中重写Thread类中的run方法,设置线程任务

        3.创建Thread类的子类对象

        4.调用Thread类中的方法start方法,开启新的线程,执行run方法

                void start( ) 是该线程开始执行:Java虚拟机调用该线程的run方法

                结果是两个线程并发的允许:当前线程(main)线程和另一个线程(创建的新线程,执行其run方法)

                多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动

                java程序属于抢占式调度

创建并启动多线程的步骤如下:
1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把
run()方法称为线程执行体。
2. 创建Thread子类的实例,即创建了线程对象
3. 调用线程对象的start()方法来启动该线程

代码:

public class Demo01 {
public static void main(String[] args) {    
//创建自定义线程对象        
MyThread mt = new MyThread("新的线程!");        
//开启新线程        
mt.start();        
//在主方法中执行for循环        
for (int i = 0; i < 10; i++) {        
System.out.println("main线程!"+i);            
}        
}    
}

 

创建多线程程序的第二种方式实现Runnable接口

Runable 接口应该由哪些打算通过某一线程执行其实例的类来实现。类必须定义一个陈伟run的无参数方法

构造方法:

public Thread(Runnable target) :分配一个带有指定目标新的线程对象
public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字

步骤如下:
1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体
2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
3. 调用线程对象的start()方法来启动线程

代码:

public class MyRunnable implements Runnable{
@Override    
public void run() {    
for (int i = 0; i < 20; i++) {        
System.out.println(Thread.currentThread().getName()+" "+i);            
}        
}    
}
public class Demo {
    public static void main(String[] args) {
        //创建自定义类对象  线程任务对象
        MyRunnable mr = new MyRunnable();
        //创建线程对象
        Thread t = new Thread(mr, "小强");
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("旺财 " + i);
        }
    }
}

 

Thread和Runnable的区别

实现Runnable接口创建多线程程序的好处:

     1.避免了单继承的局限性

            一个类只能继承一个类,类继承了Thread类就不能继承其他的类

                实现了Runnable接口,还可以继承其他的类,实现其他的接口

       2.增强了程序的扩展性,降低了程序的耦合性(解耦)

                 实现了Runnable接口的方式,把设置线程任务和开启新县城进行了分离(解耦)

                 实现类中,重写了run方法:用来设置线程任务

                 创建Thread类对象,调用start方法:用来开启新线程

 

匿名内部类方式实现线程的创建

匿名:没有名字

内部类:写在其它内部的类

匿名内部类作用:简化代码

       把子类继承父类,重写父类的方法,创建子类对象合一步完成

       把实现实现类接口,重写接口中的方法,创建实现类对象合成一步完成

匿名内部类的最终产物:子类/实现类对象,而这个类没有名字

格式:

   new 父类/接口( ){

     重复父类/接口中的方法

};

 

线程与进程

一个程序运行后至少有一个进程,一个进程中可以包含多个线程

线程属于进程,是进程中的一个执行单位,负责程序的执行

线程调度:

分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间
抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度

主线程:执行(main)方法的线程

单线程程序:java中只有一个线程

执行从main方法开始,从上到下依次执行

JVM执行main方法,main方法会进入到栈内存

JVM会找操作系统开辟一条main方法通向cpu的执行路径

cpu就可以通过这个路径来执行main方法,而这个路径有一个名字叫做main(主)线程

 

线程安全

线程同步

三种同步操作:

同步代码块

格式:

synchronized(同步锁){
     需要同步操作的代码
}

注意:

        1.通过代码块中的锁对象,可以使用任意的对象

        2.但是必须保证多个线程使用的锁对象是同一个

        3.锁对象的作用:

                 把同步代码块所著,只让一个线程在同步代码块中执行

代码:

public class Ticket implements Runnable{
private int ticket = 100;    
   
Object lock = new Object();    
/*    
 * 执行卖票操作    
 */    
@Override    
public void run() {    
//每个窗口卖票的操作         
//窗口 永远开启         
while(true){        
synchronized (lock) {            
if(ticket>0){//有票 可以卖                
//出票操作                    
//使用sleep模拟一下出票时间                     
try {                    
Thread.sleep(50);                        
} catch (InterruptedException e) {                    
// TODO Auto‐generated catch block                        
e.printStackTrace();                        
}                    
//获取当前线程对象的名字                     
String name = Thread.currentThread().getName();                    
System.out.println(name+"正在卖:"+ticket‐‐);                    
}                
}

总结:同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步

同步方法

格式:

public synchronized void method(){
   可能会产生线程安全问题的代码 
}

使用步骤:

      1.把访问了共享数据的代码块抽取出来,放到一个方法中

      2.在方法上添加synchronized修饰符

代码:

public class Ticket implements Runnable{
private int ticket = 100;    
/*    
 * 执行卖票操作    
 */    
@Override    
public void run() {    
//每个窗口卖票的操作         
//窗口 永远开启         
while(true){        
sellTicket();            
}        
}    
   
/*    
 * 锁对象 是 谁调用这个方法 就是谁     
 *   隐含 锁对象 就是  this    
 *        
 */    
public synchronized void sellTicket(){    
        if(ticket>0){//有票 可以卖  
            //出票操作
            //使用sleep模拟一下出票时间
            try {
               Thread.sleep(100);  
            } catch (InterruptedException e) {
               // TODO Auto‐generated catch block  
               e.printStackTrace();
 }
            //获取当前线程对象的名字
            String name = Thread.currentThread().getName();
            System.out.println(name+"正在卖:"+ticket‐‐);
        }
}    
}

同步锁是谁
对于非static方法,同步锁就是this
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)

静态同步方法

锁对象是谁:不能是this,this是创建对象之后产生的,静态方法优先于对象

静态方法的锁对象是本类的class属性-->class文件对象(反射)

锁机制

Lock锁

Lock接口中的方法:

public void lock() :加同步锁
public void unlock() :释放同步锁

Lock.ReecntrantLock implements Lock接口

步骤:

    1.在成员位置创建一个ReentrantLock对象

    2.在可能会出现安全问题的代码前调用Lock接口中的方法Lock获取锁

    3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

代码:

public class Ticket implements Runnable{
private int ticket = 100;    
   
Lock lock = new ReentrantLock();    
/*    
 * 执行卖票操作    
 */    
@Override    
public void run() {    
//每个窗口卖票的操作         
//窗口 永远开启         
while(true){        
lock.lock();            
if(ticket>0){//有票 可以卖            
//出票操作                 
//使用sleep模拟一下出票时间                 
try {                
Thread.sleep(50);                    
} catch (InterruptedException e) {                
// TODO Auto‐generated catch block                    
e.printStackTrace();                    
}                
//获取当前线程对象的名字                 
String name = Thread.currentThread().getName();                
System.out.println(name+"正在卖:"+ticket‐‐);                
}            
lock.unlock();            
}        
}    
}

 

你可能感兴趣的:(高级语言,java)