黑马程序员——多线程

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

线程
概念:线程是指进程中的执行流程,一个进程中可以运行多个线程。
线程总体分为:守护线程、用户线程。
线程的状态:生、死、可运行、运行、等待/阻塞。

多线程好处、弊端
好处:提高程序的效率,可以同时完成多项工作。
弊端:多线程共执行的代码块容易出现安全隐患。

1.创建线程方式

  • 1.继承Thread类。
  • 好处:直接继承Thread类中的方法,代码简单易懂。
  • 弊端:如果此类有了父类,就使用不了这种方法,类不能多继承。
  • 1.1定义类继承Thread。
  • 1.2重写run方法。
  • 1.3把新线程要做的事写在run方法中。
  • 1.4创建线程对象。
  • 1.5开启新线程,内部会自动执行run方法。

package ThreadDemo;
class MyThread extends Thread
{
    public void run()
    {
        while(true)
        {
            System.out.println("你好!");
        }
    }
}
public class MyThreadDemo
{
    public static void main(String[] args)
    {
        MyThread t = new MyThread();
        t.start();
    }
}
  • 2.实现Runnable接口
  • 好处:定义的线程类有了父类,此法方法一样可以实现。
  • 弊端:需要先获取线程对象,才能得到Thread的方法,过程比较复杂。
  • 2.1定义类实现Runnable接口。
  • 2.2实现run方法。
  • 2.3把新线程要做的事写在run方法中。
  • 2.4创建自定义的Runnable的子类对象。
  • 2.5创建Thread对象,传入Runnable。
  • 2.6调用start()开启新线程,内部会自动调用Runnable的run()方法。

package ThreadDemo;
class MyThread implements Runnable
{
    public void run()
    {
        while(true)
        {
            System.out.println("hello");
        }
    }
}
public class MyThreadDemo
{
    public static void main(String[] args)
    {
        MyThread t = new MyThread();
        new Thread(t).start();
    }
}

2Threa类常用方法

  • 1.获取名字:通过getName()方法获取线程对象的名字。
  • 2.设置线程名字:通过构造函数传入String类型的名字例如:new Thread("线程名字")。
  • 3.获取当前线程对象:Thread.currentThread()(此方法主线程也可以获取)。
  • 4.休眠:通过Thread.sleep(毫秒、纳秒),控制当前线程休眠若干毫秒。
  • 5.守护:通过setDaemon设置一个线程为守护线程。当其他非守护线程都执行结束后,自动退出。
  • 6.加入:join(),当前线程暂停,等待指定的线程执行结束后, 当前线程再继续。join(int), 可以等待指定的毫秒之后继续。

3线程同步
当有线程操作都操作多段代码块时,容易出现安全问题,所以希望执行这段代码的时候只有一个线程,当这个线程执行完后,其他线程才能来执行这个多段代码块,这时就需要同步这代码块。
1synchronized同步

1.1同步代码块

使用synchronized关键字加上一个锁对象来定义一段代码。

package ThreadDemo;
class Resouce
{
    int count = 100;
    public void out()
    {
        synchronized(this)
        {
            System.out.println(count);
            count--;
        }
    }
}
class MyThread implements Runnable
{
    private Resouce res;
    public MyThread(Resouce res)
    {
        this.res = res;
    }
    public void run()
    {
        while(true)
        {
            res.out();
        }
    }
}
public class MyThreadDemo
{
    public static void main(String[] args)
    {
        Resouce res = new Resouce();
        MyThread t1 = new MyThread(res);
        MyThread t2 = new MyThread(res);
        new Thread(t1).start();
        new Thread(t2).start();
    }
}
1.2同步方法
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的。
package ThreadDemo;
class Resouce
{
    int count = 100;
    public synchronized void out()
    {
        System.out.println(count);
        count--;
    }
}
class MyThread implements Runnable
{
    private Resouce res;
    public MyThread(Resouce res)
    {
        this.res = res;
    }
    public void run()
    {
        while(true)
        {
            res.out();
        }
    }
}
public class MyThreadDemo
{
    public static void main(String[] args)
    {
        Resouce res = new Resouce();
        MyThread t1 = new MyThread(res);
        MyThread t2 = new MyThread(res);
        new Thread(t1).start();
        new Thread(t2).start();
    }
}
2使用ReentrantLock类同步(JDK5后)

package ThreadDemo;
import java.util.concurrent.*;
class Resouce
{
    private String sThreadName;
    private int nCount = 1;
    private Boolean bFlag = false;
    private Lock lock= new ReentrantLock();
    private Condition pro = lock.newCondition();
    private Condition con = lock.newCondition();
    void set(String name) throws InterruptedException
    {
        lock.lock();
        try
        {
            while(bFlag)
            {
                pro.await();
            }    
            this.sThreadName = name+"--"+nCount++;
            System.out.println(Thread.currentThread()+"生产"+"---"+this.sThreadName);
            bFlag = true;
            con.signal();
        }
        finally
        {
            lock.unlock();
        }
    }
    void out() throws InterruptedException
    {
        lock.lock();
        try
        {
            while(!bFlag)
            {
                con.await();
            }
            System.out.println(Thread.currentThread()+"***消费"+"---"+this.sThreadName);
            bFlag = false;
            pro.signal();
        }
        finally
        {
            lock.unlock();
        }
    }
}
class Consumer implements Runnable
{
    private Resouce res;
    Consumer(Resouce res)
    {
        this.res = res;
    }
    public void run()
    {
        while(true)
        {
            try
            {
                res.out();
            }
            catch (Exception e)
            {
                System.out.println("消费商品失败!");
            }
        }
    }
}
class Produce implements Runnable
{
    private Resouce res;
    public Produce(Resouce res)
    {
        this.res = res;
    }
    public void run()
    {
        while(true)
        {
            try
            {
                res.set("商品");
            }
            catch (Exception e)
            {
                System.out.println("生产商品失败!");
            }
        }
    }
}
public class ThreadDemo2
{

    public static void main(String[] args)
    {
        Resouce res = new Resouce();
        Produce p = new Produce(res);
        Consumer c = new Consumer(res);
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(p);
        Thread t3 = new Thread(c);
        Thread t4 = new Thread(c);
        Thread t5 = new Thread(c);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}
4死锁问题
使用同步嵌套,使用了相同锁,就可能出现死锁
package ThreadDemo;
class MyThread implements Runnable
{
    private Boolean flag;
    public MyThread()
    {}
    public MyThread(Boolean flag)
    {
        this.flag = flag;
    }
    public void run()
    {
        while(true)
        {
            if(flag)
            {
                synchronized (MyThreadDemo.A)
                {
                    System.out.println("if-A锁");
                    synchronized (MyThreadDemo.B)
                    {
                        System.out.println("if-B锁");
                    }
                }
            }
            else
            {
                synchronized (MyThreadDemo.B)
                {
                    System.out.println("else-B锁");
                    synchronized (MyThreadDemo.A)
                    {
                        System.out.println("else-A锁");
                    }
                }
            }
        }
    }
}
public class MyThreadDemo
{
    static String A = "a";
    static String B = "b";
    public static void main(String[] args)
    {
        MyThread t1 = new MyThread(true);
        MyThread t2 = new MyThread(false);
        new Thread(t1).start();
        new Thread(t2).start();
    }
}
5多线程通信
多线程并发执行时,我们控制线程的有规律的执行(默认是CPU随机切换执行),就可以使用通信。

5.1JDk5以前
  • 1.wait():线程等待。
  • 2.notify():随机唤醒一个线程。
  • 3.notifyAll():唤醒所有线程。
5.2JDk5以后
  • 1.使用ReentrantLock类的newCondition()方法可以获取Condition对象。
  • 2.需要等待线程的时候使用Condition的await()方法, 需要被唤醒的线程时候用Conditon的signal()方法。
  • 3.不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了。

6.线程池(JDK5后)
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。
线程池的常见类型:固定尺寸的线程池、可变尺寸的连接池。

固定尺寸线程池

package ThreadDemo;
import java.util.concurrent.*;
class MyThread implements Runnable
{
    public void run()
    {
        System.out.println(Thread.currentThread().getName()+"执行了!");
    }
}
public class ExService
{
    public static void main(String[] args)
    {
        MyThread t = new MyThread();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        Thread t5 = new Thread(t);
        ExecutorService pool = Executors.newFixedThreadPool(2); //创建固定大小的线程池。
        pool.execute(t1); //把线程加入到线程池中。
        pool.execute(t2);
        pool.execute(t3);
        pool.execute(t4);
        pool.execute(t5);
        pool.shutdown();
    }
}
单任务线程池
只需要把pool的创建方式ExecutorService pool = Executors.newFixedThreadPool(2);改成
ExecutorService pool = Executors.newSingleThreadExecutor(); //创建固单个线程执行的线程池。
可变尺寸的线程池
只需要把pool的创建方式ExecutorService pool = Executors.newCachedThreadPool();
延迟连接池
线程在给定的延迟后运行命令,或者定期的执行
package ThreadDemo;
import java.util.concurrent.*;
class MyThread implements Runnable
{
    public void run()
    {
        System.out.println(Thread.currentThread().getName()+"执行了!");
    }
}
public class ExService
{
    public static void main(String[] args)
    {
        MyThread t = new MyThread();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        Thread t5 = new Thread(t);
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); //创建延迟线程执行的线程池。
        pool.execute(t1); //把线程加入到线程池中。
        pool.schedule(t3, 1, TimeUnit.MINUTES); //线程延迟1分钟执行。
        pool.execute(t2);
        pool.execute(t4);
        pool.execute(t5);
        pool.shutdown();
    }
}
自定义线程池
package ThreadDemo;
import java.util.concurrent.*;
class MyThread implements Runnable
{
    public void run()
    {
        System.out.println(Thread.currentThread().getName()+"执行了!");
    }
}
public class ExService
{
    public static void main(String[] args)
    {
        MyThread t = new MyThread();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        Thread t5 = new Thread(t);
        BlockingQueue<Runnable> bque = new ArrayBlockingQueue<Runnable>(20);
        ThreadPoolExecutor poo = new ThreadPoolExecutor(2, 3,2, TimeUnit.MICROSECONDS, bque);
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); //创建固单个线程执行的线程池。
        pool.execute(t1); //把线程加入到线程池中。
        pool.execute(t2);
        pool.execute(t3);
        pool.execute(t4);
        pool.execute(t5);
        pool.shutdown();
    }
}
创建自定义线程池的构造方法很多,ThreadPoolExecutor就是其中一个,这个方法的蚕食含义如下:
public ThreadPoolExecutor(int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
             TimeUnit unit,
            BlockingQueue<Runnable> workQueue)
参数:
  • corePoolSize -池中所保存的线程数,包括空闲线程。
  • maximumPoolSize -池中允许的最大线程数。
  • keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
  • unit - keepAliveTime参数的时间单位。
  • workQueue -执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
黑马程序员多线程练习题主要包括两个问题。第一个问题是如何控制四个线程在打印log之前能够同时开始等待1秒钟。一种解决思路是在线程的run方法中调用parseLog方法,并使用Thread.sleep方法让线程等待1秒钟。另一种解决思路是使用线程池,将线程数量固定为4个,并将每个调用parseLog方法的语句封装为一个Runnable对象,然后提交到线程池中。这样可以实现一秒钟打印4行日志,4秒钟打印16条日志的需求。 第二个问题是如何修改代码,使得几个线程调用TestDo.doSome(key, value)方法时,如果传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果。一种解决方法是使用synchronized关键字来实现线程的互斥排队输出。通过给TestDo.doSome方法添加synchronized关键字,可以确保同一时间只有一个线程能够执行该方法,从而实现线程的互斥输出。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [黑马程序员——多线程10:多线程相关练习](https://blog.csdn.net/axr1985lazy/article/details/48186039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值