Java学习-多线程-1-线程的创建及同步代码块

原创 2018年04月15日 14:55:08

1:什么是线程

程序按顺序执行,程序执行的线索就是一条线程!

2:创建线程的方式

创建线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。
1:继承Thread类来实现线程

此处启动线程可以使用mythread1.start()方法,也可以用一个Thread对象来承载mythread1这个对象,然后调用Thread对象的start方法,因为Thread类是实现了Runnable接口的

public class ThreadTest {
    static public void main(String... args){
        MyThread1 myThread1 = new MyThread1();
        Thread thread = new Thread(myThread1);
        thread.start();
    }
}
class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("我是一个线程");
    }
}
  • 运行结果:我是一个线程
2:实现Runnable接口来创建线程

如果是实现Runnable接口,那么就只能用Thread类来承载Mythread2对象并调用start方法来开启线程

public class ThreadTest {
    static public void main(String... args){
        MyThread2 myThread2 = new MyThread2();
        Thread thread = new Thread(myThread2);
        thread.start();
    }
}
class MyThread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("我是一个线程");
    }
}
  • 运行结果:我是一个线程

每个线程只能调用start方法一次,如果调用两次,那么编译时不会报错,运行时会报异常

3:两种创建线程方法的比较

这两种方式都能创建线程,并且都能让线程启动起来,那么为什么平时在使用的时候会用到第二种方式(实现Runnable接口)多一些呢?

  1. JAVA只支持单继承,如果设计类时还需要继承其他类,那么用继承Thread的方式就不能达到同时继承两个类
  2. JAVA可以实现多个接口,那么可以在继承了其他类的情况下通过实现接口的方式来达到创建线程
  3. 使用Runnale方式更加的面向对象,就好比我线程这个对象要start需要一个Runnable对象,你给我一个Runnable对象就行

4:原子性

原子性就是说一个操作不可以被中途cpu暂停然后调度, 即不能被中断, 要不就执行完, 要不就不执行; 如果一个操作是原子性的,那么在多线程境下,就不会出现变量被修改等奇怪的问题

如何保证一个动作的原子性呢?接下来所说的同步代码块就能保证操作原子性

5:同步代码块(synchronized)

当需要对某块逻辑进行原子性操作时,可以用这个关键字将代码框起来,这个关键字也叫对象锁,只有或得了锁得对象才能够执行该逻辑,其他得线程来到此逻辑后,就在等待池里排队,此排队时无序得,并且释放锁的对象再次抢到该锁的几率比较大

以一个卖票为例,要求三个窗口同时售卖100张票,实现代码如下:

public class ThreadTest {
    static public void main(String... args){
        Ticket ticket = new Ticket();
        for (int i = 1; i <= 3; i++ ){
            new Thread(ticket).start();
        }
    }
}
class Ticket implements Runnable{
    // 定义静态变量,总票数
    private static Integer nums = 100;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if (nums > 0){
                    System.out.println(Thread.currentThread().getName()+"已经卖出去第"+nums+"张票。");
                    // 卖出去一张票,nums数量-1
                    nums--;
                    // 此处睡眠一秒是为了让效果更加明显
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    break;
                }
            }
        }
    }
}
  • 运行结果:
    Thread-0已经卖出去第100张票。
    Thread-2已经卖出去第99张票。
    Thread-1已经卖出去第98张票。
    Thread-1已经卖出去第97张票。
    Thread-2已经卖出去第96张票。
    Thread-2已经卖出去第95张票。
    Thread-2已经卖出去第94张票。
    Thread-0已经卖出去第93张票。
    Thread-0已经卖出去第92张票。

如果去掉synchronized,那么运行会出现两个线程同时卖一张票的情况,这不符合我们的要求。例如去掉会输出这样:

Thread-1已经卖出去第100张票。
Thread-2已经卖出去第100张票。
Thread-0已经卖出去第99张票。
Thread-0已经卖出去第97张票。

显然同一张票是不能卖出去两次的,接下来我们聊聊synchronized后面的参数。这个里面可以放任意对象来当锁;为什么?首先来了解下synchronized的原理。

注意在JVM中,有个计数器的,如果调用了某个带synchronized的方法,计数器+1,一开始是0,如果在执行该方法时候又调用了其他synchronized方法,那么计数器再+1,方法执行结束返回,计数器-1,直到计数器为0,表示这个对象的锁被释放了。

由此可见,只要保证当锁的对象是同一个对象就行,而不用担心是什么对象。如:

class Cat{
}
class Ticket implements Runnable{
    // 定义静态变量,总票数
    private static Integer nums = 100;
    Cat cat = new Cat();
    @Override
    public void run() {
        while (true){
            synchronized (cat) {
                if (nums > 0) {
                    System.out.println(Thread.currentThread().getName() + "已经卖出去第" + nums + "张票。");
                    // 卖出去一张票,nums数量-1
                    nums--;
                    // 此处睡眠一秒是为了让效果更加明显
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}

自定义一只猫来当锁的对象也可以实现同步效果。

6:死锁

当一个线程获得锁之后,计数器加1,然后同步代码块里面的逻辑会去调用另一个线程,然后另一个线程的锁被其他线程拿走了,所以就会在线程等待池等待,但是另一个线程却再自己取得锁的线程等待池里面,然后就会造成死锁。

设计代码逻辑时,应当有清晰的逻辑思路,避免死锁的产生

版权声明: https://blog.csdn.net/weixin_38956287/article/details/79948749

多线程14__Java同步代码块,同步方法

同步代码块 参见http://www.cnblogs.com/sunzn/archive/2013/02/13/2910895.html,这里改为限制循环次数50次: new Thread() ...
  • aduovip
  • aduovip
  • 2015-04-25 13:17:53
  • 1429

(二) Java多线程详解之同步代码块synchronized和线程通信详解

在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源,并且对资源做了写操作时,可能会导致数据混乱。这里用了卖票的例子,代码如下:public class ThreadExampl...
  • RobertoHuang
  • RobertoHuang
  • 2017-05-17 22:06:51
  • 1116

JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this

JAVA之旅(十三)——线程的安全性, 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程)把票陆陆续续的卖完了之后,我们要反思一下,这里面有没有安全隐患呢?在实际...
  • qq_26787115
  • qq_26787115
  • 2016-06-04 18:34:42
  • 9298

java同步方法和同步代码块

java synchronized详解 记下来,很重要。 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。      一、当两个并...
  • zhenwodefengcaii
  • zhenwodefengcaii
  • 2017-01-18 14:45:38
  • 7860

Java多线程学习(吐血超详细总结)

本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。...
  • Evankaka
  • Evankaka
  • 2015-03-14 13:13:17
  • 81329

线程中的同步代码块synchronized、同步方法和同步锁Lock

在学习线程的时候,因为线程的调度具有不确定性,所以银行取钱问题、多个窗口售卖火车票问题都是反应多线程的优越性以及不确定性。当程序中有多个并发线程在进入一个代码块中并且修改其中参数时,就很有可能引发线程...
  • sinat_15274667
  • sinat_15274667
  • 2015-11-01 17:35:05
  • 3705

java多线程学习视频

  • 2017年10月30日 19:46
  • 52.95MB
  • 下载

[java多线程]多线程学习路线图(不断更新)

多线程学习路线图
  • cq1982
  • cq1982
  • 2014-08-23 17:27:47
  • 882

Java创建多线程的三种方法

Java多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没...
  • u012843873
  • u012843873
  • 2016-05-04 14:23:42
  • 1019

Java线程学习经典例子-读写者演示

基于JDK8演示了Java线程的wait与notify在多线程共享数据的用法,如何通过synchronized关键字实现对Java中的数据对象上锁...
  • jia20003
  • jia20003
  • 2016-02-14 00:24:07
  • 2447
收藏助手
不良信息举报
您举报文章:Java学习-多线程-1-线程的创建及同步代码块
举报原因:
原因补充:

(最多只允许输入30个字)