线程锁

一、多线程-同步函数的锁是this
/*
同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
所以同步函数使用的锁是this。

通过该程序进行验证。

使用两个线程来买票。
一个线程在同步代码块中。    这两个的锁不一样
一个线程在同步函数中。
都在执行买票动作。


*/


class Ticket implements Runnable
{
    private  int tick = 100;
    Object obj = new Object();
    boolean flag = true;
    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();           /*使用this锁*/
    }
    public synchronized void show()//this
    {
        if(tick>0)
        {
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
        }
    }
}


class  ThisLockDemo
{
    public static void main(String[] args) 
    {

        Ticket t = new Ticket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        try{Thread.sleep(10);}catch(Exception e){}
        t.flag = false;
        t2.start();


//        Thread t3 = new Thread(t);
//        Thread t4 = new Thread(t);
//        t3.start();
//        t4.start();


    }
}

 

二、多线程-静态同步函数的锁是class对象


/*
如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证,发现不在是this。因为静态方法中也不可以定义this。

静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
类名.class  该对象的类型是Class


静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
*/
class Ticket implements Runnable
{
    private static  int tick = 100;
    //Object obj = new Object();
    boolean flag = true;
    public  void run()
    {
        if(flag)
        {
            while(true)
            {
                synchronized(Ticket.class)           /*使用了静态,锁就变了*/
                {
                    if(tick>0)
                    {
                        try{Thread.sleep(10);}catch(Exception e){}
                        System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
                    }
                }
            }
        }
        else
            while(true)
                show();
    }
    public static synchronized void show()
    {
        if(tick>0)
        {
            try{Thread.sleep(10);}catch(Exception e){}
            System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
        }
    }
}


class  StaticMethodDemo
{
    public static void main(String[] args) 
    {

        Ticket t = new Ticket();

        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        try{Thread.sleep(10);}catch(Exception e){}
        t.flag = false;
        t2.start();


    }
}
 

三、多线程-单例设计模式-懒汉式 出现的安全问题


/*
单例设计模式。 必须要记住,面试题
饿汉式与懒汉式的区别
1.懒汉式是延时加载的形式
2.懒汉式由于是延时加载,所以在多线程中会出现安全问题
3.可以加同步函数或者同步代码块来解决,而同步函数每次都需要判断比较慢
4.这里采用同步代码块来解决安全问题
5.由于函数是静态的,所以锁是类名.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)
                    //--->A;
                    s = new Single();
            }
        }
        return s;
    }
}

class SingleDemo 
{
    public static void main(String[] args) 
    {
        System.out.println("Hello World!");
    }
}
 

 

 

四、死锁(要避免出现和熟悉)



class Test implements Runnable
{
    private boolean flag;
    Test(boolean flag)
    {
        this.flag = flag;
    }

    public void run()
    {
        if(flag)
        {
            while(true)
            {
                synchronized(MyLock.locka)
                {
                    System.out.println(Thread.currentThread().getName()+"...if locka ");
                    synchronized(MyLock.lockb)
                    {
                        System.out.println(Thread.currentThread().getName()+"..if lockb");                    
                    }
                }
            }
        }
        else
        {
            while(true)
            {
                synchronized(MyLock.lockb)
                {
                    System.out.println(Thread.currentThread().getName()+"..else lockb");
                    synchronized(MyLock.locka)
                    {
                        System.out.println(Thread.currentThread().getName()+".....else locka");
                    }
                }
            }
        }
    }
}


class MyLock
{
    static Object locka = new Object();     /*静态,能用类名直接调用*/
    static Object lockb = new Object();
}

class  DeadLockTest
{
    public static void main(String[] args) 
    {
        Thread t1 = new Thread(new Test(true));
        Thread t2 = new Thread(new Test(false));
        t1.start();
        t2.start();
    }
}

多线程(面试中的线程问题)
线程状态:
1、新建状态:当用new操作符创建一个线程时,线程还没有开始运行线程中的代码,此时线程处在新建状态。 
2、就绪状态:当线程对象调用start()方法即启动了线程,等待调度程序(thread scheduler)分配CPU时间的状态。
3运行状态(Running): 当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4.阻塞状态(Blocked):所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
 进入阻塞状态的原因:
       1>线程通过调用sleep方法进入睡眠状态;
       2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
       3>线程试图得到一个锁,而该锁正被其他线程持有;
       4>线程在等待某个触发条件;
       ......          
5.死亡状态(Dead): 1) run方法正常退出而自然死亡 2) 一个未捕获的异常终止了run方法而使线程猝死。
Java中创建线程主要有三种方式:1.继承Thread类2.实现Runnable接口3.创建Callable接口的实现类(有返回值)
 一、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程
     要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
二、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体
    同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对
         象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
三、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行
         体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask
         对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
线程终止的三种情况:
 1 线程正常执行结束。
 2 线程抛出异常且未被捕获。
 3 线程调用stop()方法终止线程,该方法已经过期。不建议使用
线程方法:
sleep():让线程进入睡眠等待一段时间,线程进入阻塞状态,时间过后进入就绪状态。
interrupt():让当前线程终止等待,进入就绪状态。
 Join():让当前线程等待加入的线程完成后才能继续执行
setPriority():设置线程优先级,最高10,最低1,默认5.
setDaemon(true):守护线程必须在调用start()方法之前,设置守护线程,当程序中只有守护程序时,该程序运行结束。
线程锁关键字:synchronized (同步)  
定义锁对象:staticfinalObjectlock=newObject();
对lock对象上锁: synchronized(lock);
对方法上锁:privatestaticsynchronizedvoiddoTask(inttid)
解决线程死锁:多个线程上锁的顺序要一致。
线程协作:
void wait():当前线程等待,等待其他线程调用对象的notify()或notifyAll()方法  
void notify():唤醒在此对象锁上等待的单个线程
void notifyAll():唤醒在此对象锁上等待的所有线程
线程池的作用:
1.提高程序的执行效率
2.控制线程的数量,防止程序崩溃
创建线程池的常用方法:
ForkJoinPool:作用充分挖掘利用多核CPU的能力,把一个大任务分解成多个小任务,执行完后再行合并。

线程池的使用方法:
1、创建一个线程池
2、创建一个Runnable对象
3、将Runnable对象作为线程任务提交到线程池中,调用线程池的submit或excute方法
4、使用完线程池后,调用线程的shutdown()方法关闭线程池(不会立马关闭 先执行完线程池中的所有已提交任务后 依次关闭线程对象 所有的线程对象进入死亡状态)
注:shutdownNow();//试图停止正在执行的线程任务,暂停正在等待的任务..但这是试图,也不一定立马关闭。
线程池的submit和excute方法的区别:
1、接收的参数不一样
2、submit有返回值,而execute没有
3、submit方便Exception处理

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值