我是靠谱客的博主 矮小冬瓜,这篇文章主要介绍Java-多线程1.几个概念2.多线程的创建3.Thread类常用方法3.实现Callable接口3.线程的调度模式4.线程安全4.4死锁5.线程池,现在分享给大家,希望可以做个参考。

1.几个概念

1.程序:指唯一完成某个任务,用某种编程语言编写的一组指令的集合

2.进程:指程序的一次执行过程,例如我们启动了qq,word等,同时可以打开多个qq,就是说一个程序可以有多个进程

3.线程:进程的进一步细分,是一个程序内部执行的一条路径,一个进程可以有多个线程,一个线程就是一个指令,cpu调度的最小单位,由cpu一条一条的执行。一个进程同时执行多个线程,那么就是多线程。

4.并行:多个cpu同时执行多个任务

5.并发:一个cpu同时执行多个任务

2.多线程的创建

1.继承Thread类

1.步骤

1.让某个类成为Thread类的子类

2.重写Thread类中的run方法,将要让该线程执行的内容写在该方法中

3.创建Thread类的对象后,调用start()方法,启动线程

class 线程类 extends Thread{

public void run(){

线程的任务

}

}

public static void main(String[] args){

线程类 线程类对象 = new 线程类();

线程类对象.start();

}

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Windows extends Thread { private int ticket=100; @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"正在卖第"+(101-ticket)); ticket--; } } public static void main(String[] args) { Windows w = new Windows(); Thread t1 = new Thread(w,"窗口1"); Thread t2 = new Thread(w,"窗口2"); Thread t3 = new Thread(w,"窗口3"); t1.start(); t2.start(); t3.start(); } }

2.注意:

如果我们要通过继承 Thread 类去实现多线程,那么我们就需要重写 Thread 类中的 run方法,然后去构建继承了 Tihread 类的对象,最后通过调用 start 方法去开启多线程。如果直接调用 run 方法就相当于在主线程 main 中调用一个类的普通的方法,并不会重新开启一个线程。

对于线程的开启我们还需要注意的是一个线程类只能调用一次 start 方法否则会抛出IllegalThreadStateException 异常。

3.Thread类常用方法

start():1.启动当前线程 2.调用线程中的 run 方法

run():通常需要重写 Thread 类中的此方法,将创建的线程要执行的操作声明在此方法中

currentThread():静态方法,返回执行当前代码的线程

getName():获取当前线程的名字

setName():设置当前线程的名字

yield():主动释放当前线程的执行权

join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去

stop():过时方法。当执行此方法时,强制结束当前线程。

sleep(long millitime):线程休眠一段时间(毫秒)

2.实现Runable接口

class 线程类 implements Runable{

public void run(){

线程的任务

}

}

public static void main(String[] args){

线程类 线程类对象 = new 线程类();

线程类对象.start();

}

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Windows implements Runnable{ private int ticket=100; @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"正在卖第"+(101-ticket)); ticket--; } } public static void main(String[] args) { Windows w = new Windows(); Thread t1 = new Thread(w,"窗口1"); Thread t2 = new Thread(w,"窗口2"); Thread t3 = new Thread(w,"窗口3"); t1.start(); t2.start(); t3.start(); } }

3.实现Callable接口

Callable与Runable类似,只是Callable的call方法有返回值

FutureTask可以用于接收Callable对象

FutureTask还提供了一个get方法用于拿取返回值

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Model4 { public static void main(String[] args) { Th1 t = new Th1(); FutureTask futureTask = new FutureTask(t); FutureTask futureTask2 = new FutureTask(t); new Thread(futureTask).start(); new Thread(futureTask2).start(); } } class Th1 implements Callable { private int num = 1; @Override public Object call() throws Exception { while (true){ synchronized (this){ notify(); if (num <= 20){ System.out.println(Thread.currentThread().getName()+"--->"+num); num++; wait(); }else { break; } } } return null; } }

3.线程的调度模式

cpu会给线程分配时间片,一但线程得到了时间片,他就可以执行,哪个线程抢到时间碎片,哪个就先执行,没有抢到时间片的就处于等待状态。

多线程的调度方式有两种即分时调度模式和抢占调度模式,其中分时调度模式是指让所有线程轮流获得 cpu 的使用权,并且分配的使用时间是平均的,而抢占模式则是指的是每条线程执行的时间、线程的切换都由系统控制,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。

3.1线程的周期

 

4.线程安全

前面我们去实现了抢票案例,但是我们发现窗口一、窗口二、窗口三在售卖同一张票,造成这种现象主要是一个线程还没结束,另外一个线程就参与进来,从而造成三个窗口操作同一张票从而出现重票。

对于上述三个窗口售票异常我们也可以称之为线程安全问题,也就是当多个线程去操作共享数据的时候出现了共享数据的冲突,此时线程时不安全的。

4.1使用线程安全类

4.2Synchronized同步锁

这个关键字可以修饰方法或代码块。

synchronized(锁对象){

代码

}

修饰方法:写在方法的返回值前。这样该方法就称为同步方法,在执行的时候,其他线程要排队等待该方法执行完毕后才执行。

public synchronized void fun(){

   //要同步的代码

}

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//同步代码块 public class Model8 { public static void main(String[] args) { WindowSal sal = new WindowSal(); Thread thread1 = new Thread(sal,"窗口一"); Thread thread2 = new Thread(sal,"窗口二"); Thread thread3 = new Thread(sal,"窗口三"); thread1.start(); thread2.start(); thread3.start(); } } class WindowSal implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ synchronized (this){ if (ticket > 0){ System.out.println(Thread.currentThread().getName()+"正在售卖第 "+(100-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。"); ticket--; }else { break; } } } } }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//同步方法 public class Windows { public static void main(String[] args) { WindowSal sal = new WindowSal(); Thread thread1 = new Thread(sal,"窗口一"); Thread thread2 = new Thread(sal,"窗口二"); Thread thread3 = new Thread(sal,"窗口三"); thread1.start(); thread2.start(); thread3.start(); } } class WindowSal implements Runnable{ private int ticket = 100; @Override public void run() { while (true){ sal(); if (ticket<=0){ break; } } } private synchronized void sal(){ if (ticket > 0){ System.out.println(Thread.currentThread().getName()+"正在售卖第"+(100-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。"); ticket--; }else { return; } } }

Synchronized特点

1)原子性,里面的代码不可分割,一起执行,一起不执行

2)有序性:被包裹的代码在多线程的条件下,只能由一条线程访问

3)可见性:每次操作其中的数据时,操作的是副本,而不是本体

4.3Lock

Lock();加锁

trylock(long 时间,时间单位);跟Lock();一样,加入了时间,但是有返回值,规定时间内加锁成功返回true,失败返回flase

unLock();释放锁

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Model3 { public static void main(String[] args) { T2 t1 = new T2("线程一"); T2 t2 = new T2("线程二"); t1.start(); t2.start(); } } class T2 extends Thread{ private static Lock lock = new ReentrantLock(); private static Condition c1 = lock.newCondition(); private static int num = 1; T2(String name){ super(name); } @Override public void run() { while (true){ try { lock.lock(); c1.signal(); if (num <= 20){ System.out.println(getName()+"--->"+num); num++; c1.await(); }else { break; } }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } } }

线程通信:利用wait()、join()、sleep()、notify()等方法实现线程中的通信

wait()-----使当前线程进入阻塞状态,并且释放锁,wait()需要抛出一个异常InterruptedException

notify()-----用于唤醒另外一个被wait()的线程。notifyall()----用于唤醒所有wait()线程。

wait()和notify()需要放在Synchronized同步方法或者代码块里面,通过锁对象调用wait()和notify()

wait();和sleep();区别

1.sleep();不会释放锁,wait();会释放

2.sleep();到时间自动苏醒,wait();需要手动唤醒

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Tongxin { public static void main(String[] args) { Tt tt1 = new Tt("线程一"); Tt tt2 = new Tt("线程二"); tt1.start(); tt2.start(); } } class Tt extends Thread { private static int num = 1; private static Object o = new Object(); public Tt() { } public Tt(String name) { super(name); } @Override public void run() { while (true) { synchronized (o) { o.notify();//-----notify()用于唤醒另外一个被wait()的线程 if (num <= 20) { System.out.println(getName() + num); num++; try { o.wait();// ---使当前线程进入阻塞状态,并且释放锁 } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } } } }

Lock和Synchronized区别

1、Synchronized是关键字,Lock是一个接口

2、Synchronized的加锁和释放锁是自动的,Lock则相反

3、Lock可以监听到它的锁状态

4、Synchronized会造成死锁,Lock则不会

4.4死锁

如有两个人吃西餐,必须要有刀和叉才能吃饭,只有一副刀叉。

如果A拿到了刀,B拿到了叉,互相都在等待另一个工具,但都不释放自己的,

这时就会造成死锁的局面,既不结束,也不继续。

造成死锁的原因:Synchronized使用不当

解决方法:1.改变加锁顺序 2.使用Lock锁

5.线程池

线程池是一种基于池化技术思想来管理线程的工具。在线程池中维护了多个线程,由线程池统一的管理调配线程来执行任务。通过线程复用,减少了频繁创建和销毁线程的开销。

最后

以上就是矮小冬瓜最近收集整理的关于Java-多线程1.几个概念2.多线程的创建3.Thread类常用方法3.实现Callable接口3.线程的调度模式4.线程安全4.4死锁5.线程池的全部内容,更多相关Java-多线程1.几个概念2.多线程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(100)

评论列表共有 0 条评论

立即
投稿
返回
顶部