Java学习笔记--多线程

  • 创建多线程的第一种方式:继承Thread类,并重写其run方法。然后通过创建Thread类的子类对象,并调用start()方法,就开启了一个新线程。
  • Thread类中有关线程名称比较常用的方法:
public Thread()//无参构造会给创建的线程默认的名称:Thread-(number)
public Thread(String name)//带参构造会给线程指定名称,使用该方法的前提是继承Thread的子类写了带参构造方法,并调用了父类的带参构造方法(也就是该方法)
public final String getName()//获取线程名称
public final synchronized void setName(String name)//设置线程名称
public static native Thread currentThread()//获取当前线程的名称
  • Thread类中有关线程优先级的常用方法:
public final int getPriority()//获取当前线程的优先级
public final void setPriority(int newPriority)//为当前线程设置优先级
//优先级的范围是:1-10.优先级高只是表示具有更高的概率得到CPU资源
  • 线程控制方面的方法:
//使线程暂停一段时间,静态方法,在线程类中调用
public static native void sleep(long millis) throws InterruptedException
//跟在线程启动方法之后,确保该线程一直拥有CPU资源,直到执行完成
public final void join() throws InterruptedException
//能够一定程度上让多个线程之间的执行更加和谐,但不能保证一人一次。静态方法,在线程类中调用
public static void yield()
//将当前线程标记为守护线程,当所有正在运行的线程都是守护线程时,Java虚拟机退出。该方法必须在线程开启之前调用设置。
public final void setDaemon(boolean on)
  • 线程生命周期:
    • 线程状态:新建(创建线程对象)、就绪(有执行资格,没有执行权)、运行(有执行资格,有执行权)、死亡(线程对象变成垃圾,等待被回收)、阻塞(没有执行资格,没有执行权)。
    • 新建到就绪通过start方法;运行到阻塞通过sleep或wait方法;阻塞到就绪通过notify方法或者sleep方法执行完毕;运行与就绪状态之间可以双向切换,因为多线程抢占资源;运行到死亡通过interrupt方法或者run方法执行完毕。
  • 创建多线程的第二种方式:实现Runnable接口,重写其中的run方法。创建该子类对象,并将其作为创建Thread对象有参构造方法的参数传递。调用Thread对象的start()方法,就开启了一个新线程。
public Thread(Runnable target)//线程按照默认命名规则
public Thread(Runnable target, String name)//线程按照传入的字符串命名
  • 第二种创建多线程的方式更好:
    • 可以避免由于Java单继承带来的局限性。
    • 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码、数据有效分离,较好的体现了面向对象的设计思想。
  • 由于多个线程操作同样的数据资源,所以有可能会产生线程不同步也就是线程安全问题。为了解决该问题,提供了同步代码块的解决方案:
synchronized(任意对象){//多个线程的该对象必须是同一个
    需要被锁起来的代码块
}
  • 也可以将一个方法中的代码都锁起来,只需要在方法前面加上修饰符synchronized,默认的对象是this。
  • 静态方法也能够通过修饰符synchronized锁起来,其默认的对象是类的字节码文件对象。
  • Collections类中的public static <T> List<T> synchronizedList(List<T> list)等方法能够实现将非同步的列表、集合、Map转换成线程同步的。
  • 除了通过synchronized关键字修饰,JDK5之后提供了一种新的对代码块进行加锁的方法。通过Lock接口的子类对象调用lock()方法和unlock()方法来对一段代码进行包裹。
  • 同步的弊端:效率低、容易产生死锁。
  • 死锁:两个或者两个以上线程在争夺资源过程中,发生的一种互相等待的现象。
public class TestDemo {
    public static void main(String[] args) {
        DieLock dl1 = new DieLock(true);
        DieLock dl2 = new DieLock(false);

        dl1.start();
        dl2.start();
    }
}

class DieLock extends Thread {

    private boolean flag;

    DieLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (MyLock.objA) {
                System.out.println("if objA");
                synchronized (MyLock.objB) {//同步代码块嵌套,这样会导致死锁的发生
                    System.out.println("if objB");
                }
            }
        } else {
            synchronized (MyLock.objB) {
                System.out.println("else objB");
                synchronized (MyLock.objA) {
                    System.out.println("else objA");
                }
            }
        }
    }
}

class MyLock {
    // 创建两把锁对象
    static final Object objA = new Object();
    static final Object objB = new Object();
}

  • Object类中与线程相关的方法:wait、notify。这两个方法的调用线程必须是处于运行状态。
  • wait方法能够使当前线程立即释放锁并进入等待阻塞状态,直到另一个线程通过调用锁对象的notify()方法或notifyAll()方法将其唤醒。被唤醒的线程不会立即开始执行,而是在锁不被占用时再与所有处于就绪状态的线程进行竞争,当争得执行权时,会从上次暂停的位置继续执行。
  • 线程组能够在成批管理线程时发挥作用。通过Thread类的getThreadGroup方法能够获得线程所在的线程组ThreadGroup对象。而ThreadGroup类中的setDaemon方法、interrupt方法等能够实现对线程组线程的统一操作。getName方法可以获取线程组的名称。在创建线程时,有一个构造方法public Thread(ThreadGroup group, Runnable target, String name)能够将创建的线程加入指定的线程组,默认线程组是main线程组。
  • 线程池相关的Executors工厂类能够创建一个线程池对象:public static ExecutorService newFixedThreadPool(int nThreads)
  • ExecutorService类的Future<?> submit(Runnable task)方法能能够接收一个Runnable接口的子类对象,并启动线程池的一个线程来运行run方法。该方法相当于将前面创建线程和start方法启动线程两步的效果。
  • ExecutorService类的<T> Future<T> submit(Callable<T> task)方法跟前面的方法相似,接收的是Callable接口的子类对象,其中泛型的类型是其中call方法的返回值类型。call方法就相当于前面的run方法,其中写上被多线程执行的代码,与前面run方法有所不同的是该方法有返回值。submit方法的返回值是Future接口的子类对象,通过调用该对象的get方法能够得到call方法的执行结果。
  • 定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定时调度的功能。
//Timer类下的方法
public Timer()//构造方法
public void schedule(TimerTask task, long delay)//在指定的延迟后安排指定的任务执行。
public void schedule(TimerTask task,long delay,long period)//在指定的延迟后安排指定的任务执行,随后开始周期重复执行该任务,重复周期由period指定
//TimerTask类下的方法,该类是抽象类,所以我们要定义子类并重写其中的run方法
public abstract void run()//指定TimerTask对象要执行的动作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值