多线程1-多线程技能

多线程学习

多线程的实现主要两种范式:继承Thread类;实现Runnable接口。
本质上,Thread类也是实现了Runnable接口。但是由于JAVA的单继承原因,通过Thread实现多线程会无法继承其他类,所以可以使用实现Runnable接口的方式达到目的。

1、线程的运行是无序的,CPU以不确定的方式运行子任务,即线程。如果在同一代码块启动多个线程,其在代码块中的start顺序也不是线程的运行顺序。

Thread类的核心用法

继承Thread类

运行类:
public class Test {
    public static void main(String[] args) {
        System.out.println("-------------Start running----------------");
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        Thread3 thread3 = new Thread3();
        thread1.start();
        thread3.start();
        thread2.start();
        System.out.println("------------End running------------");
    }
}

线程代码(1、2、3均相同,仅sleep时间不同)
public class Thread1 extends Thread{
    @Override
    public void run() {
        super.run();
        for(int i=0; i<5; i++)
            if(i == 3){
                try {
                    this.sleep(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }else{

                System.out.println("This is the first thread : "+i);
            }
    }
}

由于多线程的异步性,每次运行的结果都不相同,其中一次运行结果如下:
    -------------Start running----------------
    ------------End running------------
    This is the first thread : 0
    This is the first thread : 1
    This is the first thread : 2
    This is the third thread : 0
    This is the second thread : 0
    This is the third thread : 1
    This is the second thread : 1
    This is the third thread : 2
    This is the second thread : 2
    This is the second thread : 4
    This is the third thread : 4
    This is the first thread : 4

由以上结果可以得出,线程执行的顺序完全是随机的。

实现Runnable接口

运行类

public class Test {
    public static void main(String[] args) {
        System.out.println("-------------Start running----------------");
        Runnable1 runnable1 = new Runnable1();
        Runnable2 runnable2 = new Runnable2();
        Thread1 thread1 = new Thread1(runnable1);
        Thread2 thread2 = new Thread2(runnable2);

        runnable2.run();
        runnable1.run();
        System.out.println("------------End running------------");
    }
}

Runnable类(实际处理事务模块)
public class Runnable1 implements Runnable{

    @Override
    public void run() {
        for(int i=0; i<5; i++){
            System.out.println("This is the first thread : "+i);
        }
    }
}

Thread类
public class Thread1 extends Thread{

    public Thread1(Runnable1 runnable1) {
        System.out.println("constructor Thread1....");
    }

    public Thread1() {
        super();
    }

    @Override
    public void run() {
        super.run();
        for(int i=0; i<5; i++)
            if(i == 3){
                try {
                    this.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                System.out.println("run This is the first thread : "+i);
            }
    }
}

某个运行结果:
    -------------Start running----------------
    constructor Thread1....
    constructor Thread2....
    This is the second thread : 0
    This is the second thread : 1
    This is the second thread : 2
    This is the second thread : 3
    This is the second thread : 4
    This is the first thread : 0
    This is the first thread : 1
    This is the first thread : 2
    This is the first thread : 3
    This is the first thread : 4
    ------------End running------------


根据以上总结;
    Thread类在一开始的应用上,只重写了run方法,调用该线程时默认调用此方法。后来使用了实现Runnable接口的方法,发现Thread类拥有7个构造函数。其中包括Runnable类型的参数。所以,可以实现使用一个线程控制其他线程运行的目的。当然,这种情况下,必须重写构造函数。

实例变量与线程安全

数据不共享时,及每个线程的逻辑独立,且处理的数据独立,则不会出现安全问题。但如果出现共享数据时,不做处理则会出现脏数据读取,导致线程安全问题。例子如下:

运行类
package test2_thread;

public class test {
    public static void main(String[] args) {
        System.out.println("Thread start ... ");
        Thread1 thread1 = new Thread1();
        Thread mul1 = new Thread(thread1, "fangcheng");
        Thread mul2 = new Thread(thread1, "xuniyou");
        Thread mul3 = new Thread(thread1, "liujianfei");
        Thread mul4 = new Thread(thread1, "liuxuelian");
        Thread mul5 = new Thread(thread1, "donglixin");
        mul1.start();
        mul2.start();
        mul3.start();
        mul4.start();
        mul5.start();
        System.out.println("Thread end ... ");
    }
}

Thread类
package test2_thread;

public class Thread1 extends Thread{
    private int count = 30;

    @Override
    public void run() {
        // TODO Auto-generated method stub
        super.run();
        while(count>0){
            System.out.println("由"+this.currentThread().getName()+
                    "执行  count-- 得: "+ --count);
        }
    }

}

某个运行结果
    Thread start ... 
    由xuniyou执行  count-- 得: 28
    由xuniyou执行  count-- 得: 27
    由liujianfei执行  count-- 得: 29
    Thread end ... 
    由liuxuelian执行  count-- 得: 26
    由liuxuelian执行  count-- 得: 23
    由liuxuelian执行  count-- 得: 22
    由liuxuelian执行  count-- 得: 21
    由liuxuelian执行  count-- 得: 20
    由liuxuelian执行  count-- 得: 19
    由liuxuelian执行  count-- 得: 18
    由liuxuelian执行  count-- 得: 17
    由liuxuelian执行  count-- 得: 16
    由liuxuelian执行  count-- 得: 15
    由liuxuelian执行  count-- 得: 14
    由liuxuelian执行  count-- 得: 13
    由liuxuelian执行  count-- 得: 12
    由liuxuelian执行  count-- 得: 11
    由liuxuelian执行  count-- 得: 10
    由liuxuelian执行  count-- 得: 9
    由liuxuelian执行  count-- 得: 8
    由liuxuelian执行  count-- 得: 6
    由fangcheng执行  count-- 得: 5
    由xuniyou执行  count-- 得: 25
    由fangcheng执行  count-- 得: 3
    由liuxuelian执行  count-- 得: 4
    由donglixin执行  count-- 得: 7
    由liujianfei执行  count-- 得: 24
    由liuxuelian执行  count-- 得: 0
    由fangcheng执行  count-- 得: 1
    由xuniyou执行  count-- 得: 2

出现多线程并发中的安全问题。

为避免以上原因,采用最简单可行的方法就是加 synchronized 关键字。

修改Thread代码为:
package test2_thread;

public class Thread1 extends Thread{
    private int count = 30;

    @Override
    synchronized public void run() {
        // TODO Auto-generated method stub
        super.run();
        while(count>0){
            System.out.println("由"+this.currentThread().getName()+
                    "执行  count-- 得: "+ --count);
        }
    }

}

任何一次的结果为:
    由xuniyou执行  count-- 得: 29
    由xuniyou执行  count-- 得: 28
    由xuniyou执行  count-- 得: 27
    由xuniyou执行  count-- 得: 26
    由xuniyou执行  count-- 得: 25
    由xuniyou执行  count-- 得: 24
    由xuniyou执行  count-- 得: 23
    由xuniyou执行  count-- 得: 22
    由xuniyou执行  count-- 得: 21
    由xuniyou执行  count-- 得: 20
    由xuniyou执行  count-- 得: 19
    由xuniyou执行  count-- 得: 18
    由xuniyou执行  count-- 得: 17
    由xuniyou执行  count-- 得: 16
    由xuniyou执行  count-- 得: 15
    由xuniyou执行  count-- 得: 14
    由xuniyou执行  count-- 得: 13
    由xuniyou执行  count-- 得: 12
    由xuniyou执行  count-- 得: 11
    由xuniyou执行  count-- 得: 10
    由xuniyou执行  count-- 得: 9
    由xuniyou执行  count-- 得: 8
    由xuniyou执行  count-- 得: 7
    由xuniyou执行  count-- 得: 6
    由xuniyou执行  count-- 得: 5
    由xuniyou执行  count-- 得: 4
    由xuniyou执行  count-- 得: 3
    由xuniyou执行  count-- 得: 2
    由xuniyou执行  count-- 得: 1
    由xuniyou执行  count-- 得: 0

synchronized关键字

在run方法前添加synchronized关键字,使多个线程以排队的方式调用run方法。即加锁。
当线程1调用run时,给该方法加锁,其他线程无法调用该加锁的run方法。等待线程1执行完之后,解锁,其他线程可调用该方法。
通过以上的锁机制,达到线程安全的目的。

synchronized关键字的使用

在共享方法前加关键字即可。例子:

运行类
package test3_synchronized;
import test2_thread.Thread1;
public class test {
    public static void main(String[] args) {
        Login1 login1 = new Login1();
        Login2 login2 = new Login2();
        login1.start();
        login2.start();
    }
}

Login1类
package test3_synchronized;
public class Login1 extends Thread{ 
    @Override
    public void run() {
        super.run();
        Servlet servlet = new Servlet();
        servlet.doPost("fangcheng", "Equation");
    }

}

Login2类
package test3_synchronized;
public class Login2 extends Thread{
    @Override
    public void run() {
        super.run();
        Servlet servlet = new Servlet();
        servlet.doPost("xuniyou", "silvery");
    }       
}

servlet类
package test3_synchronized;
public class Servlet {
    private static String name;
    private static String pwd;
    private static int count = 30;
    synchronized public void doPost(String name,String pwd){
        name = name;
        pwd = pwd;
        while(count > 0)
            System.out.println("name = "+name+" pwd = "+pwd+" count : "+ --count);
    }

}

某个执行结果:
    name = xuniyou pwd = silvery count : 29
    name = fangcheng pwd = Equation count : 28
    name = xuniyou pwd = silvery count : 27
    name = fangcheng pwd = Equation count : 26
    name = xuniyou pwd = silvery count : 25
    name = fangcheng pwd = Equation count : 24
    name = xuniyou pwd = silvery count : 23
    name = fangcheng pwd = Equation count : 22
    name = xuniyou pwd = silvery count : 21
    name = fangcheng pwd = Equation count : 20
    name = xuniyou pwd = silvery count : 19
    name = fangcheng pwd = Equation count : 18
    name = xuniyou pwd = silvery count : 17
    name = fangcheng pwd = Equation count : 16
    name = xuniyou pwd = silvery count : 15
    name = fangcheng pwd = Equation count : 14
    name = xuniyou pwd = silvery count : 13
    name = fangcheng pwd = Equation count : 12
    name = xuniyou pwd = silvery count : 11
    name = fangcheng pwd = Equation count : 10
    name = xuniyou pwd = silvery count : 9
    name = fangcheng pwd = Equation count : 8
    name = xuniyou pwd = silvery count : 7
    name = fangcheng pwd = Equation count : 6
    name = fangcheng pwd = Equation count : 4
    name = fangcheng pwd = Equation count : 3
    name = xuniyou pwd = silvery count : 5
    name = fangcheng pwd = Equation count : 2
    name = xuniyou pwd = silvery count : 1
    name = fangcheng pwd = Equation count : 0

结论:满足线程安全了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值