java学习笔记(三十)java线程

java中的线程

1:多线程
(1)线程是依赖于进程而存在的。
A:进程 正在运行的应用程序
B:线程 进程的执行路径,执行单元

(2)多线程的两种方案:
继承Thread类:

public class MyThread extends Thread{
    //1.继承Thread类
    //2.重写run方法,重写run方法中的代码之后,当我们启动了这个线程之后,我们的这个线程就会执行run方法中的代码
    @Override
    public void run() {
        //需求:开启该线程之后,执行一个for循环
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
}

继承了thread类,那么这个类就变成了一个线程类,可以调用他的start方法来开启一个线程。
实现Runable接口:

public class MyThread implements Runnable{
    @Override
    public void run() {
        //启动该线程对象之后,需要执行的代码
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }   
    }
}

这里的这个类仅仅是实现了Runnalble接口的一个类,但是start方法在Thread类中,我们想要开启一个线程,就必须调用start方法,我们还是要创建一个thread对象,所以用Thread的构造方法

    public Thread(Runnable target)
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
t1.start();

(3)多线程的几个问题:

    A:启动线程用的是哪个方法
        start()
    B:start()和run()的区别
        start():1.开启线程  2.执行run()方法里面的代码
        run():执行的是线程里面执行的代码,并不会开启线程
    C:为什么要重写run()
        因为每个线程需要执行的代码都是都是不一样的,
        我们需要将每个线程自己独立执行的代码写到run()方法中执行
    D:线程可以多次启动吗
    不可以

(4)线程的调度和控制
线程休眠(Thread.sleep(毫秒值))
线程名称(setName(),getName();)
线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
什么叫线程优先级:设置你抢占cpu执行权抢占到的概率

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //线程休眠(Thread.sleep(毫秒值))
            try {
                Thread.sleep(1000);//在此处出现的异常我们只能抓取,不能抛出
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //获取执行线程的姓名
            System.out.println(this.getName()+i);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //线程名称(setName(),getName();)
        //创建3个线程对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        //给三个线程设置姓名
        t1.setName("刘备");
        t2.setName("张飞");
        t3.setName("关羽");
        //设置线程的优先级
        //线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
        //t1.setPriority(100);//设置的区间必须在1-10之间
        t1.setPriority(10);
        //开启线程
        t1.start();
        t2.start();
        t3.start(); 
    }
}

5)多线程案例:出现安全问题
5.1继承Thread卖票
5.2实现Runnable卖票(睡一会出现线程安全问题)

    问题:
    按照真实的情景加入了延迟,确发现出现了这样的两个问题:
    A:相同的票卖了多次
        CPU的一次操作必须是原子性的(操作是CPU执行一次就可以直接完成的)
    B:出现了负数的票
        随机性和延迟导致的
    出现上面的问题称为线程安全问题。
public class MyThread implements Runnable{
    int ticket = 100;
    @Override
    public void run() {
        while (true) {
            if (ticket>0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //创建MyThread这个对象
        MyThread mt = new MyThread();
        //创建3个窗口
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);
        //给线程对象设置名称
        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        //启动窗口开始售票
        t1.start();
        t2.start();
        t3.start();
    }
}
/*结果一部分:
窗口一正在出售第:2张票
窗口二正在出售第:1张票
窗口一正在出售第:0张票
窗口三正在出售第:-1张票*/

(7)如何解决多线程安全问题
* 线程安全执行效率就低
A:同步代码块(锁必须是同一个锁)

        synchronized(对象) {
            需要被同步的代码。
        }

两个问题:1.对象是什么 ?
答:任意对象 ,相当于是一把锁,只要线程进去就把锁锁上
2.需要同步的代码?
答:被线程执行的代码

Object obj = new Object();
synchronized (obj) {//这样3个线程才可以共享同一把锁   
    if (ticket>0) {
        //考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
        try {
            Thread.sleep(100);

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                    e.printStackTrace();
            }               System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");

                }
            }

B:同步方法(仅适用于实现runable接口)

        public synchronized void sellTicket(){同步代码}
同步方法:同步方法是将synchronized关键字加到方法上,同步方法的锁是this
    private synchronized void sellTicket() {    
        if (ticket>0) {
                //考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");

        }
    }

C:静态同步方法

            public static synchronized void sellTicket() {
                    需要同步的代码
            }
//静态同步方法,他的锁是本类的字节码文件对象:类名.class。
    private static synchronized void sellTicket() { 
    if (ticket>0) {
            //考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }       System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");

    }

}

(8)匿名内部类的方式使用多线程】
方式一:

    new Thread() {
        public void run() {
            ...
        }
    }.start();
new Thread(){
            //重写的方法
            @Override
            public void run() {
                for (int i = 0; i <10; i++) {
                    System.out.println(i);
                }
            }
        }.start();

方式二:

    new Thread(new Runnable(){
        public void run() {
            ...
        }
    }).start();
new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10; i++) {
                    System.out.println(i);
                }
            }
        }).start();

(9)JDK5的Lock锁

    static Lock lock = new ReentrantLock();
    枷锁:lock.lock();
    释放锁:lock.unlock();

可以让我们明确的知道在哪里加锁和释放锁。

public class MyThread implements Runnable{
    //定义100张票
    int ticket = 100;
    Object obj = new Object();
    //创建一个锁
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try{
                //加上锁,获取锁
                lock.lock();
                if (ticket>0) {
                        //考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }

                        System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
                    }
            }finally{
                //这里面的代码一定会被执行
                //释放锁
                lock.unlock();
            }
        }
    }
}

为了保证我们创建的锁一定会被释放,用一下代码进行改进
try{….}finally{…..}
把释放锁放在finally中执行,这样锁一定会被释放
(10)死锁问题
同步嵌套,锁里面套了一个锁,出现同步嵌套
(11)线程等待和唤醒机制
锁对象调用wait()锁对象调用notify()
创建锁:

public abstract class MyLock {
    public static final Object obj = new Object();
}

等待类:
public class WaitThread extends Thread{

@Override
public void run() {
    synchronized (MyLock.obj) {
        //让等待线程处于等待状态
        try {
            MyLock.obj.wait();//当线程处于等待状态的时候,线程就不会继续往下执行了
                              //线程在处于等待的时候,会释放掉自己手中的锁


        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    System.out.println("我被唤醒了");
}

}
唤醒类:

public class NotifyThread extends Thread{
    @Override
    public void run() {
        synchronized (MyLock.obj) {
            //幻想等待线程
            MyLock.obj.notify();//唤醒正在等待的线程,唤醒的等待线程的锁对象,必须和等待线程的锁对象一致
        }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        //创建等待线程,让等待线程处于一个等待状态
        WaitThread wt = new WaitThread();
        wt.start();
        //睡上5秒钟之后唤醒等待线程
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //创建唤醒线程对象
        NotifyThread nt = new NotifyThread();
        nt.start(); 
    }
}

结果:等待五秒后被唤醒。输出我被唤醒了。
注意:

    wait和sleep的区别
    wait()线程等待,在等待的同时释放锁,而sleep()方法在执行的过程中是不会释放锁的

你可能感兴趣的:(java)