【Java学习笔记】21.Java多线程

来源:互联网 发布:app生成器软件 编辑:程序博客网 时间:2024/06/01 09:26

1.线程的介绍
(1)进程:正在运行的应用程序
(2)线程:进程的执行路径,执行单元
2.线程的两种实现
(1)继承Thread类;

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

(2)实现Runnable接口

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

start()和run()的区别?
start():1.开启线程 2.执行run()方法里面的代码
run():执行的是线程里面执行的代码,并不会开启线程

线程中常用的方法:

Thread.sleep(long millis) ;//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。setName(String name);//改变线程名称 getName();//返回该线程的名称setPriority(int newPriority);//更改线程的优先级,默认值是5,区间在1-10之间。

3.多线程的安全问题
1.多线程安全问题
(1)是否是多线程环境;
(2)是否有共享数据;
(3)是否有多条语句操作共享数据。

2.如何解决多线程安全问题:
同步的代码块与锁的使用

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

注意三点
第一,对象:任意对象,这个对象就成为锁,只要线程进去就把锁锁上;
第二,需要被同步的代码:会出现线程安全问题的代码;
第三,同步代码块对锁的要求:需要多个线程共享一把锁。
(2)锁对象问题:
A:同步代码块(定义一个抽象类,里面专门定义一个锁):任意对象。
B:同步方法(仅适用于实现Runnable接口):将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:静态同步方法:它的锁是本类的字节码文件对象:类名.class。例如:synchronized (MyThread.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--+"张票");    }

匿名内部类的方式使用多线程
1.继承Thread类:

new Thread() {    public void run() {            ...    }}.start();

2.实现Runnable接口:

new Thread(new Runnable(){    public void run() {            ...    }}).start();

JDK5的Lock锁
1.Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

static Lock lock = new ReentrantLock();lock.lock();//加锁lock.unlock();//释放锁

2.void lock()获取锁。 如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。
3.void unlock()释放锁。
4.不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。为了保证我们创建的锁一定会被释放,用以下代码进行改进:

Lock l = ...;  l.lock(); try {     // access the resource protected by this lock } finally {     l.unlock(); }

5.死锁问题:同步嵌套,锁里面套了一个锁,会出现死锁问题,所以以后写代码的过程中尽量避免同步嵌套。
线程等待和唤醒机制
案例:waitThread,NotifyThread,MyLock,Test。
获取锁对象:

package com.edu_01;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():在线程休眠的过程中,不会释放手中的锁。
唤醒正在等待的线程,唤醒的等待线程的锁对象,必须和等待线程的锁对象一致。