多线程基础

1.线程与进程的关系:

​  进程是电脑中的一个事物。而线程是这个事物中的一个处理单元,进程中可能包含多个线程。

(个人观点:处理多线程是异步,单线程是同步,而同步就是阻塞模式,异步是非阻塞模式 。例子:一个车站的站点可以理解为一个进程,而经历过的车,有多种选择的行驶路线称之为线程,这就是线程和进程的关系。)

(1.1)Thread类

(1.11)通过Thread类我们可以发起一个线程

(1.12)构造函数:

Thread(ThreadStart method);//无参无返回值类型的方法Thread(ParameterizedThreadStart method)//无返回值类型,有一个object参数的方法

(1.13)属性:static CurrentThread:返回当前正在运行的线程

Name:设置/返回线程的名字

IsBackground:是否是后台线程

Priority设置优先级的

(1.14)方法:Start()启动线程,调用无参无返回值的函数

Start(Object pram)启动线程,pram传参给有参数的方法;

static Sleep(int 毫秒)让线程休眠,处于暂停执行的状态

(1.15)Abort()终止线程

Join(int 毫秒);挂起(暂停)正在调度的线程,直到被调度的线程执行完毕后,该线程才继续执行.

2.什么是多线程:

 多线程就是CPU在处理一个事物时多个单元同时执行

3.多线程会引发什么问题,及他的优缺点是什么?

​ 多线程有可能会导致数据的不同步,他的优点是能使多个任务同时进行,但是过多使用反而会降低计算机性能。(个人总结:优点:(1)线程占用的时间长可以放到后台去处理,(2)程序的运行效率高(3)在一些等待的任务实现上如用户输入,文件读取和网络收发数据等 。缺点:(1)如果有大量的线程时,就会影响它的性能(2)很多的线程时需要很多的空间(3)通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生 )

4.如何避免线程的安全问题:

​ 在java中使用synchronized关键字使代码同步,还可以使用Lock对象为必要的代码部分加锁与解锁。相比前者后者比较灵活更具扩展性。

代码案例:

无安全的线程代码:

bool bl = false;
        static void Main(string[] args)
        {
            Program p = new Program();
            Thread thread = new Thread(p.Print);
            thread.Start();
 
            p.Print();
            Console.ReadKey();
 
        }
        public void Print()
        {
                if (!bl)
                {
                    Console.WriteLine("I'm False");
                    bl = true;
                }
        }

在上面的代码运行用多次运行中,就会发现,有时候,只有一个内容,有时候又有两个内容,这是因为第一个内容未运行完,第二个内容就一起运行,而又有时候第一个为运行完成,第二个就不能运行而被堵塞了。

编写lock案例代码:

bool b1 = false;
        static readonly object locker = new object();

        static void Main(string[] args)
        {
            Program p = new Program();
            Thread thread = new Thread(p.print);
            thread.Start();

            p.print();
            Console.ReadKey();
        }

        public void print()
        {
            lock (locker)
            {
                if (!b1)
                {
                    Console.WriteLine("Hall word!");
                }
            }
        }

这个代码的运行顺序,是等第一个内容运行完成,第二个再运行所以就不像上面运行时的情况了。

5.synchronized使用的方式有几种,分别是什么,需要注意什么?

​ synchronized关键字可以加在代码块上面,格式synchronized(obj){},在代码块中执行的代码将得到同步。其中obj是同步代码块的锁对象,如果锁不是同一个对象同样会导致不同步的现象出现。     另外一种是使用同步函数,格式是public synchronized void test(){}。该种使用方式所使用的锁是当前函数所属的对象,也就是this。

6.synchronized使用在static修饰的方法上时,有什么不同?

​ 当synchronized关键字加载static修饰的 方法上时,所使用的锁是当前方法所在对象的.class对象。这是因为静态修饰的方法是随着类的加载而加载的。

7.创建线程的方式有哪些?分别有什么好处

有两种方法:

第一种:继承thread类,重写run方法,在run方法中写要执行的代码。在创建线程对象时传入子类对象。

 第二种:实现Runable接口,重写run方法。在创建线程对象时候传入该接口的子类对象。     建议在开发中都使用实现Runable接口,因为在java中不支持多继承,这样也更能体现面相对象的思想。

代码案例:

(1)无参数,无转递方法

//无参数,无转递
        static void Main(string[] args)
        {
            for (int i = 0; i<30;i++)
            {
                ThreadStart start = new ThreadStart(Calculate);
                Thread thread = new Thread(start);

                thread.Start();
            }

            Thread.Sleep(2000);
            Console.Read();
            Console.ReadKey();
        }
        public static void Calculate()
        {
            DateTime time = DateTime.Now;
            Random ra = new Random();//随机对象
            Thread.Sleep(ra.Next(10,100));//随机休眠一段时间
            Console.WriteLine(time.Minute+":"+time.Millisecond);
        }

(2)传递单个参数的方法

//传递单个参数的方法
        static void Main(string[] args)
        {
            for (int i = 0; i<30;i++)
            {
                ParameterizedThreadStart start = new ParameterizedThreadStart(Calculate);
                Thread thread = new Thread(start);

                thread.Start(i*10+10);//传递参数
            }

            Thread.Sleep(2000);
            Console.Read();
            Console.ReadKey();
        }
        public static void Calculate(object arg)
        {
            Random ra = new Random();//随机对象
            Thread.Sleep(ra.Next(10,100));//随机休眠一段时间
            Console.WriteLine(arg);
        }

(3)使用委托开启多线程方法

//使用委托开启多线程

        private delegate int NewTaskDegate(int ms);
        private static int newTask(int ms)
        {
            Console.WriteLine("任务开始!");
            Thread.Sleep(ms);
            Random random = new Random();
            int n = random.Next(10000);
            Console.WriteLine("任务完成");
            return n;
        }
        static void Main(string[] args)
        {
            NewTaskDegate task = newTask;
            IAsyncResult asyncResult = task.BeginInvoke(2000,null,null);
            int result = task.EndInvoke(asyncResult);
            Console.WriteLine(result);
            Console.ReadKey();
        }

(4)使用IAsyncResult.IsCompleted属性来判断异步调用是否完成方法

 //使用IAsyncResult.IsCompleted属性来判断异步调用是否完成方法
        private delegate int NewTaskDelegate(int ms);
        private static int newTask(int ms)
        {
            Console.WriteLine("任务开始");
            Thread.Sleep(ms);
            Random random = new Random();
            int n = random.Next(10000);
            Console.WriteLine("任务完成!");
            return n;
        }

        static void Main(string[] args)
        {
            NewTaskDelegate task = newTask;
            IAsyncResult asyncResult = task.BeginInvoke(2000,null,null);
            while (!asyncResult.IsCompleted)
            {
                Console.Write("*");
                Thread.Sleep(100);
            }

            int result = task.EndInvoke(asyncResult);
            Console.WriteLine(result);
            Console.ReadKey();
        }

8.多线程开发中如何获取当前线程的名称: currentThread.getName();

9.多线程开发中的异常信息有什么特点:**

​ 在只有main方法的时候由于只有一个线程,所以未标注线程名称,在多线程中异常信息前会标注异常的线程名称。

(1)非池化线程,就是通过自己new出来的线程,而它的主要特点是能捕获到主线程的异常,而不能捕获到子线程的一次

例如非池化线程代码案例:

static void Main(string[] args)
        {
            try
            {
                GO();
            }
            catch (NullReferenceException exception)
            {
                //代码块
            }
        }
        public static void GO()
        {
            new Thread(() =>
            {
                Thread.Sleep(1000);
                throw new NullReferenceException();
            });
        }

这样的话,就捕获不出catch里面的异常!

(2)池化线程,就是通过tack,它不但可以捕获主线程的异常,同时也是可以捕获子线程的异常。

池化线程代码案例:

 static void Main(string[] args)
        {
            GO();
            Console.ReadKey();
        }

        public static void GO()
        {
            try
            {
                Task.Run(() => throw new NullReferenceException()).Wait();
            }
            catch (AggregateException ex)
            {
                if (ex.InnerException is NullReferenceException)
                {
                    Console.WriteLine("成功的捕获到异常!");
                }
            }
        }

这样的话,就可以捕获到哪里哪里出现了错误!

10.简单描述一下线程的不同状态:

​ 多个线程同时启动的时候,每个线程大概会有五种状态,也有分成六种状态,第一种状态是创建,当newThread类或者其子类的时候,线程被创建,第二种状态是运行状态,当线程对象调用start方法线程启动并得到CPU执行权的时候,线程被执行。第三种是消亡状态,当run方法被执行完毕线程结束,或者调用stop(已过时)方法线程终止。第四种是冻结状态,或者分成睡眠状态和等待状态,线程对象调用wait方法进入等待状态(notify方法可以唤醒)。sleep(time)方法进入睡眠,时间到线程唤醒,当线程被唤醒,和start方法执行时,线程并不一定立马被执行,由于单个cpu只能一次执行一个线程,没被执行的线程则处于临时阻塞状态,但是这个时间非常短暂,cpu会随机的执行每一个线程。

线程状态的理解地址:http://img2.imgtn.bdimg.com/it/u=719130709,1176794713&fm=26&gp=0.jpg

11.什么是线程的死锁?

​ 一个线程中的同步块里面嵌套的同步块有另一个线程的同步块锁对象,对方线程同步块里面的嵌套同步块又同时拥有前一个线程里的外层同步块的锁。就会出现死锁现象。这种情况下,每个线程都拿着对方的锁不放,导致线程都卡在那不动,这种现象叫做死锁。

12.等待唤醒机制:

​ wait(); 使线程处于等待状态,其实 把线程临时存储到了线程池中;     notify();唤醒任意一个线程;     notifyAll();唤醒所有线程。     等待和唤醒必须使用在同步中。因为必须标示wait和notify所对应的锁,同一个锁对象下的只能唤醒该锁下的线程。     为什么这些方法定义在object中,因为锁可以是任意对象,而任意对象都能调用的方法必然在object中。

(1)wait()设定时间

//Wait设定等待时间
        static void Main(string[] args)
        {
            Task t = Task.Run(() =>
            {
                Thread.Sleep(500);
                Console.WriteLine("Lance");
                Thread.Sleep(500);

            });
            Console.WriteLine("t>IsCompleted="+t.IsCompleted);
            bool IsComplate = t.Wait(200);
            Console.WriteLine("wait 200毫秒后t.IsCompleted"+t.IsCompleted);
            Thread.Sleep(10000);
            Console.WriteLine("t.IsCompleted="+t.IsCompleted);

            Console.ReadKey();
        }

 

13.

在等待唤醒机制里面,我们经常会做一些条件判断让线程等待,但是如果用if()语句会存在弊端,也就是当线程被再次唤醒时候会直接执行后边代码,这样会存在不安全风险,而是要使用while()语句来开发,在线程唤醒之后再次执行条件,再判断等待或者执行后边代码。这种情况应用在多个线程同时执行时场景例如多生产多消费。

14.

使用synchronized进行同步不太灵活,当多生产多消费的时候往往要使用notifyall()方法,该方法会唤醒所有线程,这样的话就会造成程序的效率降低。如果使用多个synchronized嵌套虽然可以解决问题但是会容易造成死锁的问题。     那么怎样解决以上问题呢?在JDK1.4以前是只能那样做。在JDK1.5以后添加了Lock接口,和condition接口,Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。随着灵活性的增加,也带来了一些问题。不使用块结构锁就失去了使用 synchronized 方法和语句时会出现的锁自动释放功能。我们必须把执行代码放在一个块里面,而锁的释放给放在finnally里。详细可查API crourrent包下的lock接口。 小例子:     Lock l = new Reentrant();         l.lock();     try{     //do something....          }finally{         l.unlock();     }

15.condition接口的使用:

​ 先创建锁对象,由锁对象构建condition对象。condition对象里面包装了wait,notify,notifyAll,成为await,signal,signalAll。

(1)condition的功能

condition的功能就是这个线程把这个问题给解决完了之后,再通知另一个线程去完成其它的任务,例如:A同学扫完地,交B同学去倒垃圾一样,也就是简单的可以这样理解。

(2)condition的特性

condition可以这样简单的理解为我们对一个方法的可调用和不可以调用。

condition特性代码案例

 public static void Test1()
        {
            Console.WriteLine("Hallo World!I love Test1");
        }

        public static void test2()
        {
            Console.WriteLine("Hallo World!I love test2");
        }

        public static void test3()
        {
            Console.WriteLine("Hallo World!I love test3");
        }
        static void Main(string[] args)
        {
            Test1();
            test2();
            test3();

            Console.ReadKey();
        }

这个代码案例方法都是可以调用的,而下面,我就在它们各自的方法面前加一个[Conditional("方法")],它们运行起来就不能调用了。

[Conditional("Test")]
        public static void Test1()
        {
            Console.WriteLine("Hallo World!I love Test1");
        }

        [Conditional("Test")]
        public static void test2()
        {
            Console.WriteLine("Hallo World!I love test2");
        }

        [Conditional("Test")]
        public static void test3()
        {
            Console.WriteLine("Hallo World!I love test3");
        }
        static void Main(string[] args)
        {
            Test1();
            test2();
            test3();

            Console.ReadKey();
        }

16.

注意在使用等待唤醒机制的时候注意唤醒的是同一把锁下的线程,所以要使用锁对象下的wait,notify,notifyAll。

(1)wait的代码案例

static void Main(string[] args)
        {
            GoAsync();
            Console.WriteLine("异步同行!");
            Console.ReadKey();

        }
        static async void GoAsync()
        {
            await Task.Run(() =>
            {
                Thread.Sleep(10000);
            });
            Console.WriteLine("任务结束!");
        }

上面案例的运行结果就是打印出上面代码输出的两个内容,第一个内容提前出来,而第二个内容则是晚出来了10秒钟。

17.

多线程编程开发中最核心的安全问题就是,多段代码共享同一数据,那么就需要考虑同步。

18.sleep与wait的区别:

1.sleep方法必须制定线程休眠的时间       wait方法可以制定也可以不指定线程停止的时间。

2.sleep方法执行后不会释放监视器锁权限,此时处于临时阻塞状态。       wait方法则在执行后立即释放锁权限

 3.sleep方法到时间后自动唤醒并继续执行       wait方法则在执行后如果没有被notify或者notifyAll唤醒则一直处于等待状态。   

4.sleep方法不一定要定义在同步中       wait方法则必须在同步中使用

19.怎样让一个线程停止?

​ run方法的结束就意味着线程的结束。往往在多线程开发中,都会使用循环,我们只要控制循环的入口使其停止循环并执行结束run方法的所有代码即可终止线程。但是这里边存在些小问题,那就是当线程一旦处于wait状态后就不行了。这时候就算线程被再次唤醒,可是不会再次判断标记状态,所以还会继续执行。我们需要调用线程的interrupt方法使其强制退出中断状态即可。伴随着会跑出interruptExecption,catch后进行循环入口标记的变更线程执行完毕结束。     另一种停止的方法是调用线程的stop方法,该方法已经过时,因为有不安全因素。它会终止所有正在执行包括执行一半的线程。

20.守护线程的概念:

​ 守护线程其实指的是后台线程,我们正常创建的线程都是前台线程,当调用线程的setDaemon方法后该线程会被转换成后台线程,也就是守护线程,守护线程的作用是配合前台线程一起运行完成某些事情,而前台线程都结束后虚拟机会认为该进程结束,所有的后台线程都会结束,哪怕是后台线程仍然处于等待状态。

21.线程优先级的概念:

​ 线程在创建以后是具备默认优先级的,默认的优先级是当前线程的优先级,也就是被哪个线程创建就是默认是哪个线程的优先级(比如main主线程)。线程的优先级是使用1-10的数字表示,数字越大优先级越高。默认的优先级是5。     在Thread类中已经定义了优先级的字段。MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY。使用setPriority方法可以对线程进行优先级的设定。

22.线程组的概念:

​ 线程组就是一个集合,存放了多个单独的线程,线程组有封装的类ThreadGroup,该类里面定义了常用的一些操作方法。在创建线程的时候可以指定该线程的所属线程组。这样的好处是便于线程的管理,比如批量停止线程等。

(23)打完收工,代码案例还没有打完,如果有时间的话,我会把这个内容补全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值