java线程入门到精通,Java 多线程 -- 从入门到精通

持续更新中,欢迎收藏,关注,以便查看后续

Java 多线程 -- 从入门到精通

Java线程与线程的区别多线程的实现方法Thread中start和run方法的区别Thread和Runnable的关系使用Callable和Future创建线程线程返回值的处理方法线程池的创建使用阻塞队列拒绝策略创建多线程

线程不安全解决线程不安全(synchronized)sleep和wait的区别notify与notifyAll的区别线程的六个状态Thread.yield什么是线程安全如何实现线程安全一、synchronized二、Lock三、synchronized和Lock的区别:

Java线程与线程的区别

所有与进程相关的资源,都被记录在PCB(进程控制模块)中。进程是抢占处理机的调度单位。线程属于某个进程,共享进程的资源。线程由堆栈寄存器、程序计数器和TCB(线程控制模块)组成。线程是CPU调度的最小单位,进程是资源分配的最小单位。线程不能看做独立应用,而进程可看做独立应用进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径线程没有独立的地址空间,多进程的程序比多线程程序健壮进程的切换比线程的切换开销大

多线程的实现方法

继承Thread类,重写run方法实现Runnable接口,重写run方法通过Callable和FutureTask创建线程通过线程池创建线程

Thread中start和run方法的区别

run方法只是thread的一个普通方法调用,还是在主线程里执行,是不会开启多线程的

直接调用Run方法,程序中只有主线程这一个线程,执行路径只有一条,还是要顺序执行,需要run方法体执行完毕,才可执行下面的代码。(相当与普通的方法)

start方法可启动多线程

start方法启动线程,无需等待run方法体代码执行完毕,可以直接继续执行下面的代码。(真正的实现多线程)

代码示例

创建一个MyThread方法继承Thread

public class MyThread extends Thread {

private String name;

public MyThread(String name) {

this.name = name;

}

public void run(){

for (int i = 0 ; i < 100 ; i++){

System.out.println(this.name +"--"+ i);

}

}

}

使用run方法:结果四个方法按照顺序运行,得出结论(直接使用run方法不是多线程)

public class demo {

public static void main(String[] args) {

MyThread my1 = new MyThread("第1个线程");

MyThread my2 = new MyThread("第2个线程");

MyThread my3 = new MyThread("第3个线程");

MyThread my4 = new MyThread("第4个线程");

my1.run();

my2.run();

my3.run();

my4.run();

}

}

使用start方法:结果发现四个方法交替输出,得出结论(start真正的实现多线程)

public class demo {

public static void main(String[] args) {

MyThread my1 = new MyThread("第1个线程");

MyThread my2 = new MyThread("第2个线程");

MyThread my3 = new MyThread("第3个线程");

MyThread my4 = new MyThread("第4个线程");

my1.start();

my2.start();

my3.start();

my4.start();

}

}

Thread和Runnable的关系

Thread是实现了Runnable接口的类,是Runnable的具体实现,使得run支持多线程;因类的单一继承原则,推荐多使用Runnable接口;

创建一个MyThread方法继承Thread

public class MyRunnable implements Runnable {

private String name;

public MyRunnable(String name) {

this.name = name;

}

public void run(){

for (int i = 0 ; i < 100 ; i++){

System.out.println(this.name +"--"+ i);

}

}

}

Runnable的代码实现

public static void main(String[] args) {

MyRunnable my1 = new MyRunnable("第1个线程");

MyRunnable my2 = new MyRunnable("第2个线程");

MyRunnable my3 = new MyRunnable("第3个线程");

MyRunnable my4 = new MyRunnable("第4个线程");

Thread t1 = new Thread(my1);

Thread t2 = new Thread(my2);

Thread t3 = new Thread(my3);

Thread t4 = new Thread(my4);

t1.start();

t2.start();

t3.start();

t4.start();

}

使用Callable和Future创建线程

获取返回值示例

创建实现Callable接口的类

public class MyCallable implements Callable {

public String call() throws Exception {

return "hello world";

}

}

获取多线程返回值

@Test

public void testThread1(){

// 1.获取FutureTask对象

MyCallable myCallable = new MyCallable();

FutureTask futureTask = new FutureTask(myCallable);

// 2.开启线程

new Thread(futureTask).start();

try{

String s = (String) futureTask.get();

System.out.println(s);

}catch (InterruptedException e){

e.printStackTrace();

}catch (ExecutionException e){

e.printStackTrace();

}

}

线程返回值的处理方法

主线程等待法:循环–检测–睡眠 —》 检测要获取值不为空 停止睡眠使用Thread类的join()方法:阻塞当前线程以等待子线程处理完毕实现Callable接口:通过FutureTask或线程池获取

线程池的创建使用

阻塞队列

容量有限 基于数组的先进先出队列 BlockingQueue< Runnable > workQueue = new ArrayBlockingQueue<>(5);

容量无限 基于链表的先进先出队列 弊端:如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了 BlockingQueue< Runnable > workQueue = new LinkedBlockingQueue<>();

拒绝策略

默认 队列满了之后它将抛出 RejectedExecutionException 异常 RejectedExecutionHandler rejected = new ThreadPoolExecutor.AbortPolicy();

队列满了丢任务不异常,但是线程池将丢弃被拒绝的任务。 RejectedExecutionHandler rejected = new ThreadPoolExecutor.DiscardPolicy();

将最早进入队列的任务删除,然后将被拒绝的任务添加到等待队列中。 RejectedExecutionHandler rejected = new ThreadPoolExecutor.DiscardOldestPolicy();

如果添加到线程池失败,那么主线程会自己去执行该任务 RejectedExecutionHandler rejected = new ThreadPoolExecutor.CallerRunsPolicy();

创建多线程

四种构造方法:

/**

*  corePoolSize    核心线程数

*  maximumPoolSize 最大线程数

*  keepAliveTime   idle线程存活时间

*  unit            上个参数的单位

*  workQueue       线程对象的缓冲队列

*  threadFactory   生成线程的工厂

*  handler         达到容量后的回调

*/

//1.

ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue)

//2.

ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

RejectedExecutionHandler handler)

//3.

ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory)

//4.

ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler)

线程不安全

示例代码

public class Demo implements Runnable {

private static int sum = 0;

public void run() {

for (int i = 0;i<50000;i++) {

System.out.println(Thread.currentThread().getName() + ":" + (sum++));

}

}

public static void main(String[] args) {

Demo syncThread = new Demo();

Thread thread1 = new Thread(syncThread,"线程1");

Thread thread2 = new Thread(syncThread,"线程2");

Thread thread3 = new Thread(syncThread,"线程3");

Thread thread4 = new Thread(syncThread,"线程4");

thread1.start();

thread2.start();

thread3.start();

thread4.start();

}

}

运行截图如下:多次运行结果不一致,这就是线程不安全。正常最后一个值应该为:50000*4-1=199999

解决线程不安全(synchronized)

示例代码

public class Demo implements Runnable {

private static int sum = 0;

public void run() {

synchronized (this) {

for (int i = 0;i<50000;i++) {

System.out.println(Thread.currentThread().getName() + ":" + (sum++));

}

}

}

public static void main(String[] args) {

Demo syncThread = new Demo();

Thread thread1 = new Thread(syncThread,"线程1");

Thread thread2 = new Thread(syncThread,"线程2");

Thread thread3 = new Thread(syncThread,"线程3");

Thread thread4 = new Thread(syncThread,"线程4");

thread1.start();

thread2.start();

thread3.start();

thread4.start();

}

}

运行截图如下:最后一个值多次测试都为199999

sleep和wait的区别

基本差别

sleep是thread类的方法,wait是Object类中定义的方法。sleep()方法可以在任何地方使用。wait()方法只能在synchronized方法或者synchronized块中使用。

本质差别

Thread.sleep只会让出CPU,不会导致锁行为的改变。Object.wait不仅仅让出CPU,还会让出已经占有的同步资源锁。

示例代码:

public void run() {

synchronized (this) {

try {

//不仅仅让出CPU,还会让出已经占有的同步资源锁。

this.wait(1000);

for (int i = 0;i<50000;i++) {

System.out.println(Thread.currentThread().getName() + ":" + (sum++));

}

//只会让出CPU,不会导致锁行为的改变。

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

notify与notifyAll的区别

notify:只有一个等待线程会被唤醒而且它不能保证哪个线程会被唤醒,这取决于线程调度器。(随机)notifyAll:所有等待该锁的所有线程都会被唤醒,所有被唤醒的线程都将争夺锁,如果某一个线程获得了锁,其他线程将会进入线程等待。

生活小案例: notify: 好比你在上厕所,外面有很多人在等,但是有一个人是厕所管理员。等你出来的时候,由管理员随机找一个人去上厕所。 notifyAll: 好比你还在上厕所,外面依然有很多人在等待,但是没有厕所管理员。等你出来的时候,大家一起去抢厕所的使用权,当有一个人抢到厕所的时候,其他人重新等待空余的厕所。

线程的六个状态

初始(NEW):运行(RUNNABLE):阻塞(BLOCKED):等待(WAITING):超时等待(TIMED_WAITING):终止(TERMINATED):shang

第三步到第五步都属于阻塞状态 查看进程状态代码示例如下

public class Demo{

public static void main(String[] args) throws InterruptedException {

Thread thread = new Thread(new MyRunnable());

System.out.println(thread.getState());//线程状态:初始(NEW)

thread.start();

System.out.println(thread.getState());//线程状态:运行(RUNNABLE)

//为了保证下方方法体执行完毕,让当前主线程休眠0.1s

Thread.sleep(100);

System.out.println(thread.getState());//线程状态:终止(terminated)

}

}

class MyRunnable implements Runnable{

public void run() {

for (int i = 0; i < 100; i++) {

}

System.out.println("循环完成~");

}

}

补充:

进入synchronized时,且没有获取到锁,线程状态 ---- blocked 直到锁被释放。线程状态 ---- runnable线程调用wait()或join时,线程状态 ---- waiting 调用notify或notifyAll时,或join的线程执行结束后,线程状态 ---- runnable线程调用sleep(time),或wait(time)时,线程状态 ---- timed waiting 当休眠时间结束后,或者调用notify或notifyAll时。线程状态 ---- runnable程序执行结束,线程状态 ---- terminated

Thread.yield

使用yield线程会把CPU时间让掉,让所有线程在竞争一次,也就是说也就是谁先抢到谁执行。 代码示例如下:

public class Demo1 extends Thread {

private String name;

public Demo1(String name) {

this.name = name;

}

@Override

public void run() {

for (int i = 1; i <= 10; i++) {

System.out.println(this.name+"-------"+i);

// 当i为5时,该线程就会把CPU时间让掉,让所有线程在竞争一次,也就是说也就是谁先抢到谁执行。

if (i == 5) {

this.yield();

}

}

}

public static void main(String[] args) {

Demo1 d1 = new Demo1("张三");

Demo1 d2 = new Demo1("李四");

Demo1 d3 = new Demo1("王五");

d1.start();

d2.start();

d3.start();

}

}

什么是线程安全

在多条线程访问的时候,我们在主程序中不需要去做任何的同步,的程序还能按照我们预期的行为去执行,那么我们就可以说这个类是线程安全的。

我们什么时候需要考虑线程安全呢?:多个线程访问同一个资源 · 如果是多个线程访问同一个资源,那么就需要上锁,才能保证数据的安全性。 · 如果每个线程访问的是各自的资源,那么就不需要考虑线程安全的问题,所以这个时候,我们可以放心使用非线程安全的对象

如何实现线程安全

一、synchronized

采用synchronized关键字给代码块或方法加锁

二、Lock

在java 5之后,java.util.concurrent.locks包下提供了另外一种方式来实现线程同步,就是Lock。

三、synchronized和Lock的区别:

Lock是接口,synchronized是关键字Lock可以提高多个线程进行读操作的效率。Lock可以让等待锁的线程响应中断,使用synchronized时,等待的线程会一直等待下去,不能够响应中断发生异常时:Lock需要在finally块中释放锁,否则很可能造成死锁现象。synchronized会自动释放线程占有的锁,不会导致死锁现象发生。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值